Buenas noches amigos, llevo tiempo utilizando esta pagina para resolver mis dudas sobre php, creación de webs, etc la verdad es que es utilisima!!
Me ha surgido una duda hoy mientras trataba de ejecutar un comando python en mi web de seguridad casera. El tema es el siguiente, tengo un script pi_garage_alert.py que chequea cambios en mis puertos gpio y cuando hay un cambio lanza intentofoto.py. Intentofoto.py hace 4 fotos, envía un mail, sube las fotos a dropbox y borra una carpeta.
Cuando ejecuto en mi web boton.php (si lo asocio a pi_garage_alert.py no hace nada) si lo asocio a intentofoto.py lo único que hace es que enviar el mail, sin hacer caso a las demás ordenes. Aquí os dejo el código, tanto para que lo utiliceis como por si algún alma caritativa me puede ayudar.
El objetivo final de esto es crear un botón que active y desactive mi alarma (intentofoto.py)
intentofoto.py
#!/usr/bin/env python
import time
import os
import sys
import re
import subprocess
import datetime
import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.Utils import COMMASPACE, formatdate
from email import Encoders
###### CAPTURA DE IMAGENES############
SnapImage = "/var/www/fotospuerta/" + datetime.datetime.now().strftime("%d-%m-%Y_%H:%M:%S:%f") + ".jpg"
subprocess.call(["/usr/bin/wget","-O",SnapImage,"http://melanoma.no-ip.biz:9000/?action=snapshot"])
SnapImage = "/var/www/fotospuerta/" + datetime.datetime.now().strftime("%d-%m-%Y_%H:%M:%S:%f") + ".jpg"
subprocess.call(["/usr/bin/wget","-O",SnapImage,"http://melanoma.no-ip.biz:9000/?action=snapshot"])
SnapImage = "/var/www/fotospuerta/" + datetime.datetime.now().strftime("%d-%m-%Y_%H:%M:%S:%f") + ".jpg"
subprocess.call(["/usr/bin/wget","-O",SnapImage,"http://melanoma.no-ip.biz:9000/?action=snapshot"])
SnapImage = "/var/www/fotospuerta/" + datetime.datetime.now().strftime("%d-%m-%Y_%H:%M:%S:%f") + ".jpg"
subprocess.call(["/usr/bin/wget","-O",SnapImage,"http://melanoma.no-ip.biz:9000/?action=snapshot"])
#######ENVIO DE EMAILS#########
def sendMail(to, fro, subject, text, files=[],server="localhost"):
assert type(to)==list
assert type(files)==list
msg = MIMEMultipart()
msg['From'] = fro
msg['To'] = COMMASPACE.join(to)
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
msg.attach( MIMEText(text) )
for file in files:
part = MIMEBase('application', "octet-stream")
part.set_payload( open(file,"rb").read() )
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="%s"'
% os.path.basename(file))
msg.attach(part)
smtp = smtplib.SMTP(server)
smtp.sendmail(fro, to, msg.as_string() )
smtp.close()
sendMail(['<laplazaproducciones@gmail.com>'],'phpGeek <laplazaproducciones@gmail.com>','Alerta','La puerta de la calle ha sido abierta')
####### SUBIDA DE IMAGENES#############
os.system("/home/pi/Dropbox-Uploader/dropbox_uploader.sh -s upload /var/www/fotospuerta/ /")
os.system("rm -f /var/www/fotospuerta/*")
#time.sleep(1)
pi_garage_alert.py
#!/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()
boton.php
<?php
$command = escapeshellcmd('/var/www/pi_garage_alert.py');
$output = shell_exec($command);
echo $output;
?>
MUCHAS GRACIAS DE ANTEMANO Y LO SIENTO SI ES MUY LARGO!! :D
¿Cual es el output al ejecutar pi_garage_alert.py? ¿El usuario del proceso HTTP tiene permisos para ejecutar el proceso?
Muchas gracias por responder amigo. A que te refieres con el output? La verdad es que no se muy bien cual es el usuario del proceso html. Te adjunto el log de errores de apache2 a ver si me puedes echar una manilla.
/var/www/pi_garage_alert.py:257: RuntimeWarning: No channels have been set up yet - nothing to clean up! Try cleaning up at the end of your program instead!
GPIO.cleanup()
/var/www/fotospuerta/31-07-2014_22:15:20:311136.jpg: Permission denied
/var/www/fotospuerta/31-07-2014_22:15:20:406414.jpg: Permission denied
/var/www/fotospuerta/31-07-2014_22:36:21:010600.jpg: Permission denied
/var/www/fotospuerta/31-07-2014_22:36:21:710177.jpg: Permission denied
CRITICAL:root:Terminating due to unexpected error: <type 'exceptions.IOError'>
CRITICAL:root:Traceback (most recent call last):
File "/var/www/pi_garage_alert.py", line 162, in main
logging.basicConfig(format=log_fmt, level=log_level, filename=cfg.LOG_FILENAME)
File "/usr/lib/python2.7/logging/__init__.py", line 1528, in basicConfig
hdlr = FileHandler(filename, mode)
File "/usr/lib/python2.7/logging/__init__.py", line 901, in __init__
StreamHandler.__init__(self, self._open())
File "/usr/lib/python2.7/logging/__init__.py", line 924, in _open
stream = open(self.baseFilename, self.mode)
IOError: [Errno 13] Permission denied: '/var/log/pi_garage_alert.log'
/var/www/pi_garage_alert.py:257: RuntimeWarning: No channels have been set up yet - nothing to clean up! Try cleaning up at the end of your program instead!
GPIO.cleanup()
[Fri Aug 01 01:14:21 2014] [error] [client 192.168.1.1] PHP Fatal error: Maximum execution time of 30 seconds exceeded in /var/www/boton.php on line 4, referer: http://melanoma69.no-ip.biz/
/home/pi/Dropbox-Uploader/dropbox_uploader.sh: line 1036: echo: write error: Broken pipe
/home/pi/Dropbox-Uploader/dropbox_uploader.sh: line 1039: echo: write error: Broken pipe
Muchas gracias
Usa las etiquetas [ code][ /code] para poner códigos largos.
Esto es un problema de permisos en la carpeta /var/www/fotospuerta/ y /var/log/pi_garage_alert.log.
sudo chmod o+w /var/www/fotospuerta/
sudo chmod o+r /var/log/pi_garage_alert.log
Prueba así.
Perdon por mi ignorancia pero cuales son los codigos largos?
Despues de poner bien los permisos me siguen saliendo un monton de errores en el log de apache... que desesperacion
/var/www/pi_garage_alert.py:257: RuntimeWarning: No channels have been set up yet - nothing to clean up! Try cleaning up at the end of your program instead!
GPIO.cleanup()
[Fri Aug 01 17:22:29 2014] [error] [client 192.168.1.1] PHP Notice: Undefined index: error in /var/www/index.php on line 16
CRITICAL:root:Terminating due to unexpected error: <type 'exceptions.IOError'>
CRITICAL:root:Traceback (most recent call last):
File "/var/www/pi_garage_alert.py", line 162, in main
logging.basicConfig(format=log_fmt, level=log_level, filename=cfg.LOG_FILENAME)
File "/usr/lib/python2.7/logging/__init__.py", line 1528, in basicConfig
hdlr = FileHandler(filename, mode)
File "/usr/lib/python2.7/logging/__init__.py", line 901, in __init__
StreamHandler.__init__(self, self._open())
File "/usr/lib/python2.7/logging/__init__.py", line 924, in _open
stream = open(self.baseFilename, self.mode)
IOError: [Errno 13] Permission denied: '/var/log/pi_garage_alert.log'
/var/www/pi_garage_alert.py:257: RuntimeWarning: No channels have been set up yet - nothing to clean up! Try cleaning up at the end of your program instead!
GPIO.cleanup()
Por alguna razon necesitas mas pemisos para abrir el archivo /var/log/pi_garage_alert.log
¿Si existe el archivo verdad? Prueba darle permisos de escritura tambien:
chmod o+w /var/log/pi_garage_alert.log
Si que existe, ahora en el log no pone nada, así que supongo que eso es bueno y que esta arreglado. El problema es que no hace nada cuando pincho en mi web sobre boton.php
boton.php activa pi_garage_alert.py (cuando un magnético de puerta se activa) a su vez activa intentofoto.py
El tema es que en el terminal funciona de lujo
Gracias