Alarm Logger w/ Email Notification

Contribute source code here. Anything not accompanied by source code will be removed.

Alarm Logger w/ Email Notification

Postby danimal1228 » Tue Jan 21, 2014 3:22 pm

I wrote this because I do not have a PC running 24/7 in which to run the GUI. I wanted something that can run headless on a Raspberry Pi or any linux computer. Currently I have no need to communicate with the alarm via this program so it just logs information and sends out emails as needed. Eventually I will implement a web interface. You can set this up as a daemon to run in the background. It doesn't output anything to stdout, it just continuously appends logging information to the logfile and will send out emails for different events. Most cellular providers have a free email to sms gateway. For example, if you send an email to XXXXXXXXXX@txt.att.net, where you substitute your 10 digit phone number for the Xs, you will receive it as a text message.

I started with some of the examples that came with the software and just went from there. Many thanks to the developers for the source code and the forum. Feel free to use/modify/make suggestions. I pasted this code using the "code" tag but it might look funky. Does anyone have a better way to attach an in-line file?

Code: Select all
[size=85]
import time, logging, smtplib
from email.mime.text import MIMEText
from alarmdecoder import AlarmDecoder
from alarmdecoder.devices import SerialDevice

SERIAL_DEVICE = '/dev/ttyAMA0'      # Location of your AD2device
BAUDRATE = 115200         # Default Baud Rate
LOG_FILE = '/var/log/alarm.log'      # Make sure you have permission to write this file
EMAIL_USERNAME = 'user@server.com'   # Username to login to your SMTP server
EMAIL_PASSWORD = 'password'      # Password for your SMTP account
EMAIL_HOST = 'mail.pop.com'      # SMTP server
EMAIL_FROM = 'user@server.com'      # Your email address
EMAIL_TO = 'user@server.com'      # Destination email
SMS_TO = 'XXXXXXXXXX@txt.att.net'   # Email to SMS gateway address
EMAIL_SUBJECT = 'Alert from House Alarm!'

def main():
    try:
   # Set up logging of all INFO (or more) messages using specified format for timestamp

   logging.basicConfig(filename=LOG_FILE, level=logging.INFO, format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S')
   logging.info('---------- Starting log ----------')
   

   # Set up an event handler and open the device
   # I believe that AD2serial & AD2pi use a serial interface
   # and the AD2USB uses a USB interface
   
   device = AlarmDecoder(SerialDevice(interface=SERIAL_DEVICE))
   device.on_zone_fault += handle_zone_fault
   device.on_zone_restore += handle_zone_restore
   device.on_arm += handle_arm
   device.on_disarm += handle_disarm
   device.on_power_changed += handle_power_changed
   device.on_alarm += handle_alarm
   device.on_low_battery += handle_low_battery


        with device.open(baudrate=BAUDRATE):
            while True:
                time.sleep(1)

    except Exception, ex:
   logging.error('Exception:'+str(ex))

def handle_zone_fault(sender, zone):
   logging.info('Zone Fault:'+str(zone))

def handle_zone_restore(sender, zone):
   logging.info('Zone Restore:'+str(zone))
   
def handle_arm(sender):
   logging.info('Armed')
   Send_Email(EMAIL_FROM, SMS_TO, EMAIL_SUBJECT, 'Alarm-Armed')

def handle_disarm(sender):
   logging.info('Disarmed')
   Send_Email(EMAIL_FROM, SMS_TO, EMAIL_SUBJECT, 'Alarm-Disarmed')

def handle_power_changed(sender, status):
   if status:   #if panel on AC power then
      pStatus = 'Panel on AC power'
   else:      #if panel on Battery power then
      pStatus = 'Panel on Battery Power'

   logging.warning(pStatus)
   Send_Email(EMAIL_FROM, SMS_TO, EMAIL_SUBJECT, pStatus)

def handle_alarm(sender, status):
   if status:   #if alarm was just triggered
      aStatus = 'Alarm Triggered'
   else:      #if alarm was just cancelled
      aStatus = 'Alarm Cancelled'

   logging.warning(aStatus)
   Send_Email(EMAIL_FROM, SMS_TO, EMAIL_SUBJECT, aStatus)

def handle_low_battery(sender, status):
   logging.warning('Low Battery:'+str(status))
   Send_Email(EMAIL_FROM, EMAIL_TO, EMAIL_SUBJECT, 'Alarm-Low Battery')

def Send_Email(FromAddr, ToAddr, MsgSub, MsgText):
   msg = MIMEText(MsgText)
   msg['To'] = ToAddr
   msg['From'] = FromAddr
   msg['Subject'] = MsgSub

   server = smtplib.SMTP(EMAIL_HOST)
   server.login(EMAIL_USERNAME, EMAIL_PASSWORD)
   server.sendmail(FromAddr, ToAddr, msg.as_string())
   server.quit()

if __name__ == '__main__':
    main()
[/size]
Last edited by danimal1228 on Fri Jan 24, 2014 4:53 pm, edited 1 time in total.
danimal1228
Junior Nut
Junior Nut
 
Posts: 20
Joined: Sat Jan 11, 2014 9:35 am

Re: Alarm Logger w/ Email Notification

Postby kevin » Tue Jan 21, 2014 3:50 pm

Thanks for the submission, I have moved this over to the Code Contributions section.
Not an employee of the company. Just here to help and keep things clean.
kevin
Platinum Nut
Platinum Nut
 
Posts: 994
Joined: Fri Aug 16, 2013 10:10 am

Re: Alarm Logger w/ Email Notification

Postby danimal1228 » Wed Jan 22, 2014 8:43 pm

I updated this script to include the option of ignoring certain zones. For example, I have 3 doors and 2 motions on my alarm. One of the motions is the main living area and it sees motion constantly throughout the day. There is no point of constantly logging that zone so I included the option to exclude zones of your choice.

Add this line at the top near the variable declarations:
Code: Select all
IGNORE_ZONE_LIST = [4,5]


Change the "handle_zone_fault" function to the following:
Code: Select all
def handle_zone_fault(sender, zone):
        for z in IGNORE_ZONE_LIST:
                if zone == z: return
       
        logging.info('Zone Fault:'+str(zone))


Change the "handle_zone_restore" function to the following:
Code: Select all
def handle_zone_restore(sender, zone):
        for z in IGNORE_ZONE_LIST:
                if zone == z: return
       
        logging.info('Zone Restore:'+str(zone))
danimal1228
Junior Nut
Junior Nut
 
Posts: 20
Joined: Sat Jan 11, 2014 9:35 am

Re: Alarm Logger w/ Email Notification

Postby jrnyldo » Wed Feb 12, 2014 5:52 am

I am currently running the base PI Raspbian Image downloaded from teh NuTech site. I would like to add this Email capability to my system. I need the Alarmdecoder running so I can use my tablet as a kepad but I would like to add this email functionality to the PI. Any guidance on how to do this? I am a windows developer but don't have a lot of experience with the Linux platform.
jrnyldo
newt
newt
 
Posts: 4
Joined: Wed Feb 12, 2014 5:45 am

Re: Alarm Logger w/ Email Notification

Postby Scott » Wed Feb 12, 2014 9:52 am

jrnyldo wrote:I am currently running the base PI Raspbian Image downloaded from teh NuTech site. I would like to add this Email capability to my system. I need the Alarmdecoder running so I can use my tablet as a kepad but I would like to add this email functionality to the PI. Any guidance on how to do this? I am a windows developer but don't have a lot of experience with the Linux platform.


Howdy.

Since you're running our pre-configured image the device is already exposed on port 10000 through ser2sock so you can use it from multiple applications. You can make a couple easy tweaks to danimal's code to have it use a SocketDevice instead of directly talking to the serial port.

Take a look at this for an example.

As an aside, if you're running the 2013-12-31 version of our image I'd recommend upgrading the alarmdecoder python package from 0.5 to 0.6, as it fixes an ugly cpu-use issue:

Code: Select all
sudo pip install alarmdecoder --upgrade
Scott
Expert Nut
Expert Nut
 
Posts: 118
Joined: Thu Dec 12, 2013 11:17 am

Re: Alarm Logger w/ Email Notification

Postby ppc » Wed Feb 12, 2014 8:19 pm

Thanks for this great contribution!

You can add a neat feature to this using the LRR messages by adding

Code: Select all
device.on_lrr_message += handle_lrr_message


to main() and a handler that pulls the disarm code used to identify who disarmed (or armed) the system. I use a big gnarly if statement for matching a code number to a real name, but there's probably a cleaner way of doing that.

Something along these lines:
Code: Select all
def handle_lrr_message(sender, message):
   if message.event_type == 'OPEN':
      if message.event_data == '000':
         USERNAME = 'keypad'
      elif message.event_data == '001':
         USERNAME = 'installer'
      elif message.event_data == '002':
         USERNAME = 'Me'
      elif message.event_data == '003':
         USERNAME = 'Wife'
      elif message.event_data == '004':
         USERNAME = 'KidA '
      elif message.event_data == '005':
         USERNAME = 'KidB'
      else:
         USERNAME = 'Unknown'
      lrrText = "Alarm disarmed by {0}".format(USERNAME) 
      logging.warning(str(lrrText))
      Send_Email(EMAIL_FROM, SMS_TO, EMAIL_SUBJECT, lrrText)
ppc
newt
newt
 
Posts: 2
Joined: Tue Jan 07, 2014 7:47 pm

Re: Alarm Logger w/ Email Notification

Postby jrnyldo » Tue Apr 08, 2014 12:11 pm

Well, I tried this and am oh so close. I get a couple of my email messages and then it just locks up at the point it is trying to log into the email account. Any ideas? I put in the logout as a last ditch attempt. Not sure if it is required or not.
Code: Select all
import time, logging, smtplib
from email.mime.text import MIMEText
from alarmdecoder import AlarmDecoder
from alarmdecoder.devices import SocketDevice
#from smtplib import SMTP
#from smtplib import SMTP_SSL
#from smtplib import SMTPException

# Configuration values
HOSTNAME = 'localhost'
PORT = 10000

LOG_FILE = '/var/log/alarm.log'      # Make sure you have permission to write this file
EMAIL_USERNAME = 'myemail@yahoo.com'   # Username to login to your SMTP server
EMAIL_PASSWORD = 'password'      # Password for your SMTP account
EMAIL_HOST = 'smtp.mail.yahoo.com'      # SMTP server
EMAIL_FROM = 'myemail@yahoo.com'      # Your email address
EMAIL_TO = '5555555555@vtext.com'      # Destination email
SMS_TO = '5555555555@vtext.com'   # Email to SMS gateway address
EMAIL_SUBJECT = 'Alert from House Alarm!'

def main():
    try:
   #

   # Set up an event handler and open the device
   # I believe that AD2serial & AD2pi use a serial interface
   # and the AD2USB uses a USB interface
   
        device = AlarmDecoder(SocketDevice(interface=(HOSTNAME, PORT)))
   
        device.on_zone_fault += handle_zone_fault
        device.on_zone_restore += handle_zone_restore
        device.on_arm += handle_arm
        device.on_disarm += handle_disarm
        device.on_power_changed += handle_power_changed
        device.on_alarm += handle_alarm
        device.on_low_battery += handle_low_battery


        with device.open():
            while True:
                time.sleep(1)

    except Exception as ex:
       print ('Exception',ex)
       #logging.error('Exception:'+str(ex))

def handle_zone_fault(sender, zone):
    print ('Zone Fault in Zone: ',zone)
   #logging.info('Zone Fault:'+str(zone))

def handle_zone_restore(sender, zone):
        print ('Zone Restore in Zone: ',zone)
   #logging.info('Zone Restore:'+str(zone))
   
def handle_arm(sender):
    print ('Alarm is armed')
   #logging.info('Armed')
    Send_Email(EMAIL_FROM, SMS_TO, EMAIL_SUBJECT, 'Alarm-Armed')
    print ('Armed Email was successfully sent')

def handle_disarm(sender):
    print ('Alarm is DisArmed')
   #logging.info('Disarmed')
    Send_Email(EMAIL_FROM, SMS_TO, EMAIL_SUBJECT, 'Alarm-Disarmed')
    print ('DisArmed email was successfully sent')

def handle_power_changed(sender, status):
   if status:   #if panel on AC power then
      pStatus = 'Panel on AC power'
   else:      #if panel on Battery power then
      pStatus = 'Panel on Battery Power'

   #logging.warning(pStatus)
   Send_Email(EMAIL_FROM, SMS_TO, EMAIL_SUBJECT, pStatus)
   print ('Status is: ',pStatus)

def handle_alarm(sender, status):
   if status:   #if alarm was just triggered
      aStatus = 'Alarm Triggered'
   else:      #if alarm was just cancelled
      aStatus = 'Alarm Cancelled'

   #logging.warning(aStatus)
   Send_Email(EMAIL_FROM, SMS_TO, EMAIL_SUBJECT, aStatus)
   print ('In Handle Alarm, email was successful: ',aStatus)
   
def handle_low_battery(sender, status):
   #logging.warning('Low Battery:'+str(status))
   Send_Email(EMAIL_FROM, EMAIL_TO, EMAIL_SUBJECT, 'Alarm-Low Battery')

def Send_Email(FromAddr, ToAddr, MsgSub, MsgText):
   msg = MIMEText(MsgText)
   msg['To'] = ToAddr
   msg['From'] = FromAddr
   msg['Subject'] = MsgSub

   try:

       server = smtplib.SMTP_SSL(EMAIL_HOST,465)
       print ('SMTP Server is created')
       #server.ehlo()
       #server.starttls()
       #server.ehlo()
       server.login(EMAIL_USERNAME, EMAIL_PASSWORD)
       print ('Logged in to smtp server')
       server.sendmail(FromAddr, ToAddr, msg.as_string())
       print ('sent message to smtp server')
       server.logout()
       print ('logged out of smtp server')
       server.close()
       print ('SMTP server is now closed')
   except SMTPException as ex:
       print ('Exception in Send_Email: ',ex)
if __name__ == '__main__':
    main()
jrnyldo
newt
newt
 
Posts: 4
Joined: Wed Feb 12, 2014 5:45 am

Re: Alarm Logger w/ Email Notification

Postby mathewss » Tue Apr 08, 2014 12:49 pm

It worked for me with a few exceptions.

1. I had to remove the .logout() this function did not work with the following exception
('Exception', AttributeError("SMTP_SSL instance has no attribute 'logout'",))
2. I had to uncomment out the Exception import from the lib for this error
('Exception', NameError("global name 'SMTPException' is not defined",))


You say you got a few messages then it stopped. I think your debugging is good it should show all steps in sending the email. If its stopping at logging in then I suspect something is blocking your email or yahoo is using a tar pit.

Try a different host such as gmail to confirm or use tcpdump to capture the traffic and see what is going on.

Re
Sean M
mathewss
Moderator
Moderator
 
Posts: 188
Joined: Fri Dec 06, 2013 11:14 am

Re: Alarm Logger w/ Email Notification

Postby jrnyldo » Wed Apr 09, 2014 8:10 am

If I run it and then arm my security system, I get the Text Message that it was armed. I then disarm the system and it gets to the point where it says SMTP server created. It hangs for a while and then eventually gives me an error, 4.3.2 Internal server error (6530). It will then send a valid Armed text message. I can do this serveral times and it may be the Arm or the disarm message that gets the failure. Any ideas? By the way, I removed the items you mentioned such as the SMTPException and the Logout.
jrnyldo
newt
newt
 
Posts: 4
Joined: Wed Feb 12, 2014 5:45 am

Re: Alarm Logger w/ Email Notification

Postby Scott » Wed Apr 09, 2014 10:02 am

I have a couple ideas:

You might try .quit() instead of .close(). Using .close() will just close the socket while .quit() sends a QUIT to the server. Yahoo may not like the fact that you aren't shutting your sessions down correctly in addition to the fact that not conforming to the RFC for SMTP is generally indicative of spam.

If that doesn't fix it, a little googling says that an SMTP 4.3.2 error typically indicates that you should try sending the email again. Seems to occur for a variety of reasons, but I'm guessing yahoo is likely throttling sends to prevent spam.

If you're running this on a Raspberry Pi (or another linux box) you might try installing sendmail and sending via localhost to see if you see the same issue. You could also set up an email account on gmail and try sending through them.

If you want to go a little more crazy you could handle the exceptions and queue it up for sending later, or set up an account with Twilio and skip the email gateway entirely.
Scott
Expert Nut
Expert Nut
 
Posts: 118
Joined: Thu Dec 12, 2013 11:17 am

Next

Return to Code Contributions

Who is online

Users browsing this forum: No registered users and 7 guests