#!/usr/bin/python2.7
import RPi.GPIO as GPIO
import time
import subprocess
import re
import sys
import logging
import smtplib
import os
import httplib2
from sleekxmpp.xmlstream import resolver, cert
import ssl
import traceback
from time import strftime
from datetime import timedelta
sys.path.append('/usr/local/etc')
import pi_garage_alert_config as cfg
##############################################################################
# Sensor support
##############################################################################
def get_garage_door_state(pin):
"""Returns the state of the garage door on the specified pin as a string
Args:
pin: GPIO pin number.
"""
if GPIO.input(pin):
state = 'open'
else:
state = 'closed'
return state
def get_uptime():
"""Returns the uptime of the RPi as a string
"""
with open('/proc/uptime', 'r') as uptime_file:
uptime_seconds = int(float(uptime_file.readline().split()[0]))
uptime_string = str(timedelta(seconds=uptime_seconds))
return uptime_string
def get_gpu_temp():
"""Return the GPU temperature as a Celsius float
"""
cmd = ['vcgencmd', 'measure_temp']
measure_temp_proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
output = measure_temp_proc.communicate()[0]
gpu_temp = 'unknown'
gpu_search = re.search('([0-9.]+)', output)
if gpu_search:
gpu_temp = gpu_search.group(1)
return float(gpu_temp)
def get_cpu_temp():
"""Return the CPU temperature as a Celsius float
"""
cpu_temp = 'unknown'
with open("/sys/class/thermal/thermal_zone0/temp", "r") as temp_file:
cpu_temp = float(temp_file.read()) / 1000.0
return cpu_temp
def rpi_status():
"""Return string summarizing RPi status
"""
return "Temperatura CPU: %.1f, Temperatura GPU: %.1f, Tiempo Encendido Pi: %s" % (get_gpu_temp(), get_cpu_temp(), get_uptime())
##############################################################################
# Logging and alerts
##############################################################################
def send_alerts(logger, alert_senders, recipients, subject, msg):
"""Send subject and msg to specified recipients
Args:
recipients: An array of strings of the form type:address
subject: Subject of the alert
msg: Body of the alert
"""
for recipient in recipients:
if recipient[:6] == 'email:':
alert_senders['Email'].send_email(recipient[6:], subject, msg)
else:
logger.error("Unrecognized recipient type: %s", recipient)
##############################################################################
# Misc support
##############################################################################
def truncate(input_str, length):
"""Truncate string to specified length
Args:
input_str: String to truncate
length: Maximum length of output string
"""
if len(input_str) < (length - 3):
return input_str
return input_str[:(length - 3)] + '...'
def format_duration(duration_sec):
"""Format a duration into a human friendly string"""
days, remainder = divmod(duration_sec, 86400)
hours, remainder = divmod(remainder, 3600)
minutes, seconds = divmod(remainder, 60)
ret = ''
if days > 1:
ret += "%d days " % (days)
elif days == 1:
ret += "%d day " % (days)
if hours > 1:
ret += "%d hours " % (hours)
elif hours == 1:
ret += "%d hour " % (hours)
if minutes > 1:
ret += "%d minutes" % (minutes)
if minutes == 1:
ret += "%d minute" % (minutes)
if ret == '':
ret += "%d seconds" % (seconds)
return ret
##############################################################################
# Main functionality
##############################################################################
class PiGarageAlert(object):
"""Class with main function of Pi Garage Alert"""
def __init__(self):
self.logger = logging.getLogger(__name__)
def main(self):
"""Main functionality
"""
try:
# Set up logging
log_fmt = '%(asctime)-15s %(levelname)-8s %(message)s'
log_level = logging.INFO
if sys.stdout.isatty():
# Connected to a real terminal - log to stdout
logging.basicConfig(format=log_fmt, level=log_level)
else:
# Background mode - log to file
logging.basicConfig(format=log_fmt, level=log_level, filename=cfg.LOG_FILENAME)
# Banner
self.logger.info("==========================================================")
self.logger.info("Encendiendo Alarma de la Puerta de Entrada")
# Use Raspberry Pi board pin numbers
self.logger.info("Configurando Ajustes Globales")
GPIO.setmode(GPIO.BOARD)
# Configure the sensor pins as inputs with pull up resistors
for door in cfg.GARAGE_DOORS:
self.logger.info("Configurando pin %d para \"%s\"", door['pin'], door['name'])
GPIO.setup(door['pin'], GPIO.IN, pull_up_down=GPIO.PUD_UP)
# Last state of each garage door
door_states = dict()
# time.time() of the last time the garage door changed state
time_of_last_state_change = dict()
# Index of the next alert to send for each garage door
alert_states = dict()
# Read initial states
for door in cfg.GARAGE_DOORS:
name = door['name']
state = get_garage_door_state(door['pin'])
door_states[name] = state
time_of_last_state_change[name] = time.time()
alert_states[name] = 0
self.logger.info("Estado Inicial de \"%s\" es %s", name, state)
status_report_countdown = 5
while True:
for door in cfg.GARAGE_DOORS:
name = door['name']
state = get_garage_door_state(door['pin'])
time_in_state = time.time() - time_of_last_state_change[name]
# Check if the door has changed state
if door_states[name] != state:
door_states[name] = state
time_of_last_state_change[name] = time.time()
self.logger.info("Estado de \"%s\" ha cambiado a %s despues de %.0f sec", name, state, time_in_state)
#if state == 'open':
os.system("python /home/pi/python/intentofoto.py")
#os.system("/home/pi/Dropbox-Uploader/dropbox_uploader.sh -s upload /var/www/fotospuerta/ /")
#os.system("rm -f /var/www/fotospuerta/*")
# Reset alert when door changes state
if alert_states[name] > 0:
# Use the recipients of the last alert
recipients = door['alerts'][alert_states[name] - 1]['recipients']
send_alerts(self.logger, alert_senders, recipients, name, "%s esta ahora %s" % (name, state))
alert_states[name] = 0
# Reset time_in_state
time_in_state = 0
# See if there are more alerts
if len(door['alerts']) > alert_states[name]:
# Get info about alert
alert = door['alerts'][alert_states[name]]
# Has the time elapsed and is this the state to trigger the alert?
if time_in_state > alert['time'] and state == alert['state']:
send_alerts(self.logger, alert_senders, alert['recipients'], name, "%s ha sido %s %d seconds!" % (name, state, time_in_state))
alert_states[name] += 1
# Periodically log the status for debug and ensuring RPi doesn't get too hot
status_report_countdown -= 1
if status_report_countdown <= 0:
status_msg = rpi_status()
for name in door_states:
status_msg += ", %s: %s/%d/%d" % (name, door_states[name], alert_states[name], (time.time() - time_of_last_state_change[name]))
self.logger.info(status_msg)
status_report_countdown = 600
# Poll every 1 second
time.sleep(1)
except KeyboardInterrupt:
logging.critical("Terminating due to keyboard interrupt")
except:
logging.critical("Terminating due to unexpected error: %s", sys.exc_info()[0])
logging.critical("%s", traceback.format_exc())
GPIO.cleanup()
if __name__ == "__main__":
PiGarageAlert().main()