Python script not runnig on boot - raspberry - python

I made a python script on my raspberry - /home/pi/bin/script.py:
#!/usr/bin/python
from urllib2 import urlopen
from time import sleep
FILE = "publicip"
SITE = "http://ipecho.net/plain"
DELAY = 60 # seconds
def get_ip():
while True:
# wait for DELAY seconds
sleep(DELAY)
# get my current public ip
try:
ip = urlopen(SITE).read()
except IOError:
continue
# compare with the one in file
file_value = open(FILE).read()
if ip != file_value: # if they are not equal
open(FILE, "w").write(ip) # update the ip in file
if __name__ == "__main__":
get_ip()
It's propose is to get my public ip and store it in a file.
I need this script run in a loop so it can update the file as soon
the ip changes.
If the power fail I want it to run when the raspberry restarts. So,
I updated the /etc/rc.local file:
#!/bin/sh -e
/home/pi/bin/script.py
exit 0
After that I used sudo reboot to restart the raspberry. I'm using PuTTY
from a windows computer to connect to the raspberry. After logging in again
I used ps -e | grep script.py to see if my script was runnig but it was
not. Then I runned the script manually and it worked!
What would you do to solve this problem?

An alternative for running in the cron or init, it to use a userspace monitor.
This tutorial is great showing supervisor.
It is really easy to use.
apt-get install supervisor
service supervisor restart
add to /etc/supervisor/conf.d/ip_update.conf
[program:ip_update]
command=/home/pi/bin/script.py
autostart=true
autorestart=true
stderr_logfile=/var/log/ip_update.err.log
stdout_logfile=/var/log/ip_update.out.log
and you can still use supervisorctl to manage it:
$ supervisorctl
> restart ip_update

First verify your script execution's permission, if it's have a execution permission. After that, you need use & after command of script in (runs to infinite loops), try:
#!/bin/sh
/home/pi/bin/script.py &
More details in raspbian documentation.

Your other option is to use cron
sudo crontab -e will open the crontab for you
you can set your script to run as often as you like and if you put in the entry:
#reboot /home/pi/bin/script.py
it should run during the boot sequence
other non numeric options are:
#yearly Run once a year, "0 0 1 1 *".
#annually (same as #yearly)
#monthly Run once a month, "0 0 1 * *".
#weekly Run once a week, "0 0 * * 0".
#daily Run once a day, "0 0 * * *".
#midnight (same as #daily)
#hourly Run once an hour, "0 * * * *"
The standard entry is:
# Minute Hour Day of Month Month Day of Week Command
0 * * * * /home/pi/bin/script.py
#Once an hour on the hour
* * * * * /home/pi/bin/script.py
#Every minute
Edit:
with reference to your comment, performing it with cron, means that you should take out the timing within your code, as that is what cron is doing. So you would end up up with something like:
#!/usr/bin/python
from urllib2 import urlopen
FILE = "publicip"
SITE = "http://ipecho.net/plain"
def get_ip():
try:
ip = urlopen(SITE).read()
except IOError:
continue
# compare with the one in file
file_value = open(FILE).read()
if ip != file_value: # if they are not equal
open(FILE, "w").write(ip) # update the ip in file
if __name__ == "__main__":
get_ip()
With reference to your existing code, I notice that you are never closing the file, just an endless loop of opening it with read and then opening it with write, I'm not sure quite how python will handle that offhand but it certainly isn't good practice.

Related

How do I fix crontab executing a python subprocess incorrectly?

I am running a python file that, among other things, opens and runs a subprocess that runs the 'speedtest-cli --simple' command line. When executed normally, this works fine and returns the ping, download and upload, but when called with crontab, the values are blank. Values are normally only blank if an internet connection can not be established.
#!/usr/bin/env python
#Speed test opens a subprocess that runs the speed test from the command terminal, returning ping, download and upload speeds.
def speedTest():
cmd = 'speedtest-cli --simple' #Command to be run in terminal
envi = os.environ
response = subprocess.Popen(cmd, env=envi, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.read().decode('utf-8')#Run subprocess
try: #Try to pull relevant data
ping = re.findall('Ping:\s(.*?)\s', response, re.MULTILINE)
download = re.findall('Download:\s(.*?)\s', response, re.MULTILINE)
upload = re.findall('Upload:\s(.*?)\s', response, re.MULTILINE)
except: #-2 Error code for Speedtest Error, entered if pull fails (NOTE: even if the pull isn't able to connect, the try should still succeed. This case should not be entered.)
print('failed')
ping = ['-2']
download = ['-2']
upload = ['-2']
errflag = -2 #Set errflag to -2 to track error
pass
if (ping == []): #-3 Error code for Speedtest Connection Error. Entered if pull is successful but connection could not be made.
ping.append('-3')
download.append('-3')
upload.append('-3')
errflag = -3 #Set errflag to -3 to track error
return [ping[0],download[0],upload[0]] #Return ping, download, and upload.
the crontab that runs it is shown here
Edit this file to introduce tasks to be run by cron.
#
# Each task to run has to be defined through a single line
# indicating with different fields when the task will be run
# and what command to run for the task
#
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').
#
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
#
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
#
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h dom mon dow command
* * * * * /home/pi/datatest.py >> out.txt 2>&1
Any suggestions on how to obtain output values in crontab would be much appreciated.

Bad Display Name when running Python script on boot with Touch Screen

Attempting to run a Python script on boot on Raspberry Pi 3B+ 1GB RAM, Raspbian, with a SunFounder 10" Touch Screen, - .log file returns "Bad display name'
Python script is 100% functional when run via Terminal / executable script / Thonny etc. Attempted to run at boot first via rc.local - created a service, enabled service, daemon-reload... etc. Did not work.
Tried to run as crontab, same result - .log output from crontab shows "Bad display name". Thought it was lack of Display Environment imported and declared within the Python script, so I added that - but on boot returns the same result.
This is the Python Script I'm using
#!/usr/bin/env python3
import RPi.GPIO as GPIO
import os
import sys
import webbrowser
import time
import subprocess
from pynput import keyboard
from Xlib.display import Display
#GPIO Readout
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
#GPIO Header Setup
header = 2
GPIO.setup(header, GPIO.IN)
#Omxplayer Commands
Loop = 'omxplayer -b --loop --no-osd -o hdmi /home/pi/Videos/PlanetEarth.mp4 > /dev/null'
Donation = 'omxplayer -b --no-osd -o hdmi /home/pi/Videos/Cartoon.mp4 > /dev/null'
KillPlayer = 'pkill omxplayer.bin'
KillForm = 'pkill chromium'
#Set Display Environment
new_env = dict(os.environ)
new_env['DISPLAY'] = ':0.0'
#Form Handling Required Below
#If Donation is successful, Stop Looping Video, Open Form in Chromium, Wait 60 seconds, Close Chromium, Restart Loop
def PullDownSuccess():
subprocess.Popen(KillPlayer, env=new_env, shell=True)
time.sleep(2)
webbrowser.open('<url>')
time.sleep(60)
subprocess.Popen(KillForm, env=new_env, shell=True)
time.sleep(2)
subprocess.Popen(Loop, env=new_env, shell=True)
#Inception
subprocess.Popen(Loop, env=new_env, shell=True)
#Terminate Loop with Escape Key or Manually Initiate Donation Success
def on_press(key):
if key == keyboard.Key.ctrl:
PullDownSuccess()
if key == keyboard.Key.esc:
subprocess.Popen(KillPlayer, shell=True)
#Keyboard Listener Module
with keyboard.Listener(
on_press=on_press) as listener:
listener.join()
#Donation Successful Do:
while True:
header_state = GPIO.input(header)
if header_state == GPIO.HIGH:
PullDownSuccess()
I am currently attempting to run this script on Boot via crontab with this line:
#reboot (/bin/sleep 10; /usr/bin/python3 /home/pi/Custom_Scripts/<script>.py > /home/pi/Custom_Scripts/cronjoblog 2>&1)
The error log file for crontab returns the following:
raise error.DisplayNameError(display)
Xlib.error.DisplayNameError: Bad display name ""
This error only exists when attempting to run script on boot up. Is the Display overriding the boot display permissions on Boot Up? What is keeping the script from running on the Display on boot up, but not when remotely executed? Thank you for your consideration.
Update: Still no solution. Display environment returns ":0.0' ... so far I have tried to remove
> /dev/null from #Omxplayer Commands
Replacing crontab startup line to:
DISPLAY=":0" /usr/bin/python3 /home/pi/Custom_Scripts/<script>.py
and
DISPLAY=":0.0" /usr/bin/python3 /home/pi/Custom_Scripts/<script>.py
And any possible combination of these.
Confirmed the script is not waiting for any background processes as I have added delay (time.sleep) up to 30 seconds, as well as returning IP addresses, etc.
Returns either Bad Display Name still OR "Can't connect to display ":0": b'Invalid MIT-MAGIC-COOKIE-1 key"
Still looking for a solution if anyone has one.
EDIT:
Fixed using /LXDE-pi/autostart. Answer below.
Try to add DISPLAY environment variable before calling your script:
DISPLAY=":0" /usr/bin/python3 /home/pi/Custom_Scripts/<script>.py
DISPLAY="/dev/null" /usr/bin/python3 /home/pi/Custom_Scripts/<script>.py
Have you tried a longer sleep?
If you try to get your ip adress during your startup script with (for example) :
import socket
def get_ip_address():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
return s.getsockname()[0]
you will get an errror like:
File "/usr/lib/python2.7/socket.py", line 228, in meth
return getattr(self._sock,name)(*args)
error: [Errno 101] Network is unreachable
This is because at reboot network is not necessarily available. In order to make it available
see https://raspberrypi.stackexchange.com/questions/45769/how-to-wait-for-networking-on-login-after-reboot :
sudo raspi-config
then select option 3 Boot Options
then select option 4 Wait for network at boot
Once you have done that networking should not be an issue at reboot.
You get also check for other ways to enforce network setup before running your script take a look at :
https://askubuntu.com/questions/3299/how-to-run-cron-job-when-network-is-up
If there are unforeseen ramifications for using this method, I will update this thread accordingly. I fixed this issue just by adding two lines to the autostart file which resides in /etc/sdg/lxsession/LXDE-pi/autostart.
sudo nano /etc/xgd/lxsession/LXDE-pi/autostart
Add these two lines, do not modify the existing code.
sleep 5
#/usr/bin/python3 /home/pi/Custom_Scripts/<script>.py
My autostart file looks like this:
#lxpanel --profile LXDE-pi
#pcmanfm --desktop --profile LXDE-pi
#xscreensaver -no-splash
point-rpi
sleep 5
#usr/bin/python3 /home/pi/Custom_Scripts/<script>/py
Ctrl + O to write file
sudo reboot

python script run failed in crontab while meeting local()

I'm trying to run a python script in crontab.
5 0 * * * python /home/hadoop/import_openapp.py >> /home/hadoop/openapp.out 2>&1
The python script is something like below:
import sys
import datetime
from fabric.api import local
ystd = datetime.date.today() - datetime.timedelta(days=1)
c = ystd.strftime('%Y-%m-%d')
print(c)
print('Start to format file ...')
......
print('Start to upload on HDFS ...')
local("/home/hadoop/hadoop/bin/hadoop fs -put " + finalfile + " /user/hadoop/yunying/openapp")
print('Start to upload on MaxCompute ...')
......
When the crontab is called, the log file is like:
2016-07-01
Start to format file ...
Start to upload on HDFS ...
[localhost] local: /home/hadoop/hadoop/bin/hadoop fs -put /data/uxin/nsq_client_active_collect/hadoop/openappfinal.log /user/hadoop/yunying/openapp
And then, the process is over. I cannot find it in ps -ef|grep python
Why it comes to an end while meeting local()?
It is likely that the PYTHONPATH it not set up correctly for whatever the user that CRON is using to run the script. Print out the path to a debug file to check:
with open('/path/to/debug_file.txt', 'wt') as f:
f.write(str(sys.path))
Try adding the line:
sys.path.append("/path/to/fabric.api")
before importing local
You can also dynamically get the location of the file that is being run using
import os
os.path.realpath(__file__)
this will allow you to use relative paths if you need them

How to append crontab entries using python-crontab module?

I would like to ask for help with the python-crontab module. I have a simple shell script to record an internet radio stream using the curl command. I want to schedule recordings ahead by scheduling them in a crontab. I found the python-crontab module that alows me to write directly to crontab. But each time I schedule a new recording the older crontab entry is over-written. Is it possible to write persistant crontab entries using the python-crontab?
I simplified my code to demonstrate my problem:
from crontab import CronTab
def get_recording_parameters(Min,Hour,day,month,job_number):
radio_cron = CronTab()
cmd = "sh /home/pifik/Documents/record_radio.sh"
cron_job = radio_cron.new(cmd, comment='job_'+str(job_number))
cron_job.setall(Min, Hour, day, month, None)
radio_cron.write()
If I run it with the following parameters: get_recording_parameters(0,22,23,12,1), and check the crontab in Terminal with the crontab -l command I get 0 22 23 12 * sh /home/pifik/Documents/record_radio.sh # job_1.
If I run it again with different parameters, for example: get_recording_parameters(10,23,25,12,2) and check the crontab with crontab -l I get 10 23 25 12 * sh /home/pifik/Documents/record_radio.sh # job_2, the job 1 is overwritten.
I tried to change the 3rd line of code to radio_cron = CronTab(tabfile='/home/pifik/Documents/filename.tab') and it helps that all new entries are appended in the filename.tab but nothing is written to the crontab.
I am running Ubuntu 14.04 and Python 3.4.3.
It looks like each time you add a job and write it back out it's overwriting what was already in the crontab. I read the documentation and I can't make heads or tails out of it. It seems you should be able to read in what was already there and add to it but for the life of me I can't figure out how from the docs.
You can get around that issue by re-working it as a class that puts together all the jobs before it writes them back out. Of course that paints you into the same corner you're already in which is appending doesn't work (unless you lay in the old entries again before writing):
#!/home/sklassen/py/try-pycrontab/env/bin/python
from crontab import CronTab
class CronSet:
def __init__(self):
self._crontab = CronTab()
def add_job(self, min, hour, day, month, job_number):
cmd = "sh /home/pifik/Documents/record_radio.sh"
job = self._crontab.new(cmd, comment='job'+str(job_number))
job.setall(min, hour, day, month, None)
def save(self):
self._crontab.write()
def main():
c = CronSet()
c.add_job(0, 22, 23, 12, 1)
c.add_job(0, 23, 23, 12, 2)
c.save()
if __name__ == '__main__':
main()
# running 'crontab -l' produces the following
# 0 22 23 12 * sh /home/pifik/Documents/record_radio.sh # job1
# 0 23 23 12 * sh /home/pifik/Documents/record_radio.sh # job2
I modified Steven's code to make it work for me.
In order not to lose the previously scheduled crontab jobs I create a cron_jobs.txt file and copy all existing scheduled jobs from crontab to cron_jobs.txt with the subprocess call "crontab -l > /home/pifik/Documents/cron_jobs.txt". Every time I do it I overwrite everything in the file. Then I create a new recording job and append it to the cron_jobs.txt file. After that I overwrite the crontab by running "subprocess.call('crontab /home/pifik/Documents/cron_jobs.txt', shell=True)".
This is a workaround to make it work but I am still interested to know if it is possible to use the python-crontab module to append new jobs directly without the need for the cron_jobs.txt file. Arguably, the way I do it now, it could be also done without the module but the module makes it easier for me to further manage the jobs by activating or deactivating them and deleting the expired jobs. I will put some tkinter GUI on it and I'll be done for now.
import subprocess
from crontab import CronTab
class CronSet:
def __init__(self):
self._crontab = CronTab(tabfile="/home/pifik/Documents/cron_jobs.txt")
def add_job(self, minute, hour, day, month, title):
subprocess.call('crontab -l > /home/pifik/Documents/cron_jobs.txt', shell=True)
choice=input('''1. Cesky Rozhlas 1
2. Cesky Rozhlas 2
Enter your choice (1 or 2): ''')
length = int(input("Enter the length of recording in minutes: "))*60
if choice ==str(1):
stream = "http://amp.cesnet.cz:8000/cro1-256.ogg"
else:
stream = "http://amp.cesnet.cz:8000/cro2-256.ogg"
cmd = "curl %s -m %i -o /home/pifik/Documents/Recordings/%s.ogg" %(stream, length, title)
job = self._crontab.new(cmd, comment=title)
job.setall(minute, hour, day, month, None)
def save(self):
self._crontab.write()
def main():
c = CronSet()
month = input("Enter month(1-12): ")
day = input("Enter day(1-31): ")
hour = input("Enter hour(0-24): ")
minute = input("Enter minute(0-59): ")
title = input("Enter title of recording: ")
c.add_job(minute, hour, day, month, title)
c.save()
subprocess.call('crontab /home/pifik/Documents/cron_jobs.txt', shell=True)
if __name__ == '__main__':
main()
The issue is that you haven't specified any user or filename for CronTab to load from. So it doesn't. The bug is that it writes out the empty crontab to the user by default, even if you don't specify the user.
The existing documentation says:
from crontab import CronTab
empty_cron = CronTab()
my_user_cron = CronTab(user=True)
users_cron = CronTab(user='username')
Which is correct in that you're making an empty crontab. So I've gone ahead and committed a fix and a test to make sure it causes an error if you try and write an empty_cron without specifying the user or filename.
Please add user=True to your code to make it work as you expect.

Raspberry Pi background program runs but doesnt work

SOLVED: For some reason making CRON run a bash script that runs the python script solved the problem.
I have a python script "temperature.py" which is an infinite loop that checks values from a GPIO pin, adds the values to a file which it uploads to google drive with "gdrive" and sometimes sends a mail using smtp. The script works perfectly if i run it from the SSH terminal ($ sudo python temperature.py) but it doesn't work at startup like i would want it to.
I'm using raspbian wheezy.
What I've done:
in /etc/rc.local:
#...
#...
sleep 10
python /home/pi/temperature.py&
exit 0
the pi boots normally and after i login using SSH and write:
...$ps aux
i get:
...
root 2357 1.4 1.9 10556 8836 ? S 21:11 0:12 python /home/pi/temperature.py
...
so I'm guessing it is running and it uses 1.4% CPU which is very little but almost all other processes use 0.0%. Since the program probably
It doesnt do anything however... my google drive is empty...
So it works if i run it from terminal as background but not if i run it from rc.local...
What I'm guessing :
it lacks some permission?
it must be something with rc.local... since it works perfectly from terminal
The result of
...$ls -l temperature.py
-rwxr-xr-x 1 root root 1927 Dec 12 21:10 temperature.py
...$ls -l /etc/rc.local
-rwxr-xr-x 1 root root 373 Dec 12 20:54 /etc/rc.local
I have tried staring it using cron ($sudo crontab -e) but it didn't work either.
Any ideas? I feel like I'm missing something obvious but since I'm very new to raspberry pi and linux stuff I can't find it on google.
The script temperature.py
#Made by Matthew Kirk
# Licensed under MIT License, see
# http://www.cl.cam.ac.uk/freshers/raspberrypi/tutorials/temperature/LICENSE
#Adapted by me
import RPi.GPIO as GPIO
import time
import subprocess
import os
import commands
import sys
import smtplib
from email.mime.text import MIMEText
print 'TEMPERATURE LOGGER - M'
print ' '
#MAILER SETUP
to = '****#gmail.com'
gmail_user = '****#gmail.com'
gmail_password = '*****'
smtpserver = smtplib.SMTP('smtp.gmail.com',587)
#TEMP LOGGER GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(7,GPIO.IN)
while True:
print 'fail'
if GPIO.input(7):
break
while GPIO.input(7):
pass
waitTime = 60
tempTreshold = 50
logFile = "/home/pi/tDat.csv"
while True:
dataFile = open(logFile,"a")
time_1 = time.time()
tFile = open("/sys/bus/w1/devices/28-011582ac5dff/w1_slave")
text = tFile.read();
tFile.close();
tData = text.split("\n")[1].split(" ")[9]
temp = float(tData[2:])
temp = temp/1000
timeStamp = time.strftime("%d/%m/%Y %H:%M:%S")
dataFile.write(str(temp)+","+ timeStamp+ "\n")
dataFile.close()
file_ID = commands.getoutput('drive list | tail -n +2 | head -1 | awk \'{print $1;}\' ')
cmd = 'drive delete --id '+file_ID
os.system( cmd )
cmd = 'drive upload --file '+logFile
os.system( cmd )
# MAIL IF TEMP TOO LOW
if temp < tempTreshold:
smtpserver.ehlo()
smtpserver.starttls()
smtpserver.ehlo()
smtpserver.login(gmail_user,gmail_password)
msg = MIMEText('The temperature in Branten, {}C, is below {} degrees C!!!'.format(temp,tempTreshold)+'\n'+'Recorded$
msg['Subject'] = 'Branten Temperature Warning'
msg['From'] = gmail_user
msg['To'] = to
smtpserver.sendmail(gmail_user,[to],msg.as_string())
smtpserver.quit()
sys.exit()
and the CRON:
* * * * * python /home/pi/temperature.py
Consider revising your code to not use an infinate loop.
Read about Linux CRON jobs. CRON is a service that will execute your program or script on a schedule (properly). EDIT: it is installed by default on most linux distros including Rasbian.
Some good examples
Maybe nohup helps?
nohup python /home/pi/temperature.py&

Categories

Resources