Reputation: 31
I have a script for my raspberry pi that continually takes readings and saves data to a file. However I know this is dangerous as there is a real risk of the SD card becoming corrupted if the power is pulled while data is being saved.
Is there a way I can have the script terminate itself if the computer is inactive for a set amount of time?
Sorry if this question is vague but I have no idea where to even begin with this so I can't show any code that I have tried.
Upvotes: 3
Views: 5510
Reputation: 414179
You could call libXss.XScreenSaverQueryInfo()
function to get the idle time in the same way as a screen saver does in X. Pure Python pxss.py
shows how it can be done. Download it and put it alongside your code:
#!/usr/bin/env python
from __future__ import division
import os
import signal
import time
# $ wget https://raw.githubusercontent.com/mariano/snakefire/9437e59ffe9deff83676c5101a0dbf694ad3344b/snakefire/pxss.py
import pxss
def watchdog(timeout, idle_callback):
"""Call *idle_callback* after *timeout* seconds of inactivity."""
tracker = pxss.IdleTracker(idle_threshold=timeout*1000)
while True:
state_change, wait_time, _ = tracker.check_idle()
if state_change == "idle":
idle_callback()
time.sleep(wait_time / 1000)
def die():
"""thread.interrupt_main() analog."""
os.kill(os.getpid(), signal.SIGINT)
Example:
import sys
import threading
from itertools import cycle
# exit after 10 seconds of inactivity
t = threading.Thread(target=watchdog, args=[10, die])
t.daemon = True
t.start()
# do whatever you like here that can be interrupted by Ctrl+C
try:
for c in cycle('/|\-'):
sys.stderr.write('\b' + c)
sys.stderr.flush()
time.sleep(.2)
except KeyboardInterrupt:
print('bye')
Upvotes: 0
Reputation: 612
I wrapped xprintidle for python, I'd be keen to see if it does what you want!
https://pypi.python.org/pypi/xprintidle
To install:
pip install xprintidle
To use:
import xprintidle
print xprintidle.idle_time()
Let me know how you go :)
Upvotes: 0
Reputation: 6387
xprintidle
utility, can make this an easy task:
#!/usr/bin/env python
# encoding: utf-8
import subprocess as sp
import threading
import time
import sys
def quit_on_idle(threshold=2000):
import thread
# note that sys.stdout.write and .flush should rather be used
# instead of print
while True:
try:
idle = float(sp.check_output('xprintidle').strip())
if idle > threshold:
print 'Inactive for {} seconds'.format(idle / 1000)
break
except (ValueError, sp.CalledProcessError) as err:
print 'An error occured'
# add your error handling here
time.sleep(0.2)
thread.interrupt_main()
try:
threading.Thread(target=quit_on_idle).start()
print 'Watchdog has been started in a separate thread'
time.sleep(1)
print 'one second passed'
time.sleep(3)
print 'This may not run at all, if you left computer idle'
except KeyboardInterrupt:
print 'Bye'
Upvotes: 1
Reputation: 23711
That is a naive watchdog implementation:
import os
import signal
import threading
class Watchdog():
def __init__(self, timeout=10):
self.timeout = timeout
self._t = None
def do_expire(self):
os.kill(os.getpid(),signal.SIGKILL)
def _expire(self):
print("\nWatchdog expire")
self.do_expire()
def start(self):
if self._t is None:
self._t = threading.Timer(self.timeout, self._expire)
self._t.start()
def stop(self):
if self._t is not None:
self._t.cancel()
self._t = None
def refresh(self):
if self._t is not None:
self.stop()
self.start()
Build it by wd = Watchdog()
and every time you get something that feed your work call wd.refresh()
. If you don't call refresh before timeout ends it will call os.kill(os.getpid(),signal.SIGKILL)
.
You cannot use just sys.exit()
because it raise just a SystemExit
exception: use kill
works as you want.
Now you can use something to poll the system and use the answer to refresh or not the watch dog. For instance xprintidle
tell to you the X idle time, but all depend from what you need to monitoring.
Use example
timeout=10
wd = Watchdog(timeout)
wd.start()
while True:
a=str(raw_input('Tell me something or I will die in {} seconds: '.format(timeout)))
wd.refresh()
print("You wrote '{}'... you win an other cycle".format(a[:-1))
Upvotes: 1