]> www.vanbest.org Git - motioneye-debian/commitdiff
fixed email notifications not being sent; reorganized imports; added
authorCalin Crisan <ccrisan@gmail.com>
Tue, 20 Oct 2015 19:27:06 +0000 (22:27 +0300)
committerCalin Crisan <ccrisan@gmail.com>
Tue, 20 Oct 2015 19:58:42 +0000 (22:58 +0300)
settings for more subprocess timeouts

16 files changed:
extra/motioneye.conf.sample
motioneye/cleanup.py
motioneye/config.py
motioneye/handlers.py
motioneye/mediafiles.py
motioneye/meyectl.py
motioneye/mjpgclient.py
motioneye/motionctl.py
motioneye/sendmail.py
motioneye/server.py
motioneye/settings.py
motioneye/smbctl.py
motioneye/thumbnailer.py
motioneye/tzctl.py
motioneye/utils.py
motioneye/wsswitch.py

index 50cb251a50cbb7ac731daf8446dc65d781165290..2503a1c6c72a19cc450fe74396dce3253a5de2ad 100644 (file)
@@ -81,8 +81,14 @@ enable_reboot false
 # timeout in seconds to use when talking to the SMTP server
 smtp_timeout 60
 
+# timeout in seconds to wait media files list
+list_media_timeout 120
+
 # timeout in seconds to wait for zip file creation
 zip_timeout 500
 
+# timeout in seconds to wait for timelapse creation
+timelapse_timeout 500
+
 # enable adding and removing cameras from UI
 add_remove_cameras true
index f63520075d790b388358a68e0dbd24c97c418f9a..5f93ea3dbec7223c0a45b9814ccc3dc5a66802ab 100644 (file)
@@ -20,7 +20,8 @@ import logging
 import multiprocessing
 import os
 import signal
-import tornado
+
+from tornado.ioloop import IOLoop
 
 import mediafiles
 import settings
@@ -35,8 +36,8 @@ def start():
         return
 
     # schedule the first call a bit later to improve performance at startup
-    ioloop = tornado.ioloop.IOLoop.instance()
-    ioloop.add_timeout(datetime.timedelta(seconds=min(settings.CLEANUP_INTERVAL, 60)), _run_process)
+    io_loop = IOLoop.instance()
+    io_loop.add_timeout(datetime.timedelta(seconds=min(settings.CLEANUP_INTERVAL, 60)), _run_process)
 
 
 def stop():
@@ -63,17 +64,17 @@ def running():
 def _run_process():
     global _process
     
-    ioloop = tornado.ioloop.IOLoop.instance()
+    io_loop = IOLoop.instance()
     
     if thumbnailer.running():
         # postpone if thumbnailer is currently running
-        ioloop.add_timeout(datetime.timedelta(seconds=60), _run_process)
+        io_loop.add_timeout(datetime.timedelta(seconds=60), _run_process)
         
         return
         
     else:
         # schedule the next call
-        ioloop.add_timeout(datetime.timedelta(seconds=settings.CLEANUP_INTERVAL), _run_process)
+        io_loop.add_timeout(datetime.timedelta(seconds=settings.CLEANUP_INTERVAL), _run_process)
 
     if not running(): # check that the previous process has finished
         logging.debug('running cleanup process...')
index 2dc62b894742440ee233cf5f37858429e1750767..603bfabd97c0fdc6b63183df251a8fd1c9ff00ae 100644 (file)
@@ -34,8 +34,6 @@ import update
 import utils
 import v4l2ctl
 
-from utils import OrderedDict
-
 
 _CAMERA_CONFIG_FILE_NAME = 'thread-%(id)s.conf'
 _MAIN_CONFIG_FILE_NAME = 'motion.conf'
@@ -1326,7 +1324,8 @@ def restore(content):
             def later():
                 powerctl.reboot()
 
-            IOLoop.instance().add_timeout(datetime.timedelta(seconds=2), later)
+            io_loop = IOLoop.instance()
+            io_loop.add_timeout(datetime.timedelta(seconds=2), later)
 
         else:
             logging.info('invalidating config cache')
@@ -1428,7 +1427,7 @@ def _python_to_value(value):
 
 
 def _conf_to_dict(lines, list_names=[], no_convert=[]):
-    data = OrderedDict()
+    data = utils.OrderedDict()
     
     for line in lines:
         line = line.strip()
@@ -1467,7 +1466,7 @@ def _conf_to_dict(lines, list_names=[], no_convert=[]):
 
 def _dict_to_conf(lines, data, list_names=[]):
     conf_lines = []
-    remaining = OrderedDict(data)
+    remaining = utils.OrderedDict(data)
     processed = set()
     
     # parse existing lines and replace the values
@@ -1677,7 +1676,7 @@ def get_additional_structure(camera, separators=False):
                 'with' if separators else 'without'))
 
         # gather sections
-        sections = OrderedDict()
+        sections = utils.OrderedDict()
         for func in _additional_section_funcs:
             result = func()
             if not result:
@@ -1694,7 +1693,7 @@ def get_additional_structure(camera, separators=False):
             
             logging.debug('additional config section: %s' % result['name'])
     
-        configs = OrderedDict()
+        configs = utils.OrderedDict()
         for func in _additional_config_funcs:
             result = func()
             if not result:
index 7f0f761b0399eee0b1ca8ecd9b5e6689fa9d17d7..69f89127adc43d6273cd159ba726218728898ed8 100644 (file)
@@ -23,8 +23,8 @@ import re
 import socket
 import subprocess
 
-from tornado.web import RequestHandler, HTTPError, asynchronous
 from tornado.ioloop import IOLoop
+from tornado.web import RequestHandler, HTTPError, asynchronous
 
 import config
 import mediafiles
@@ -356,8 +356,8 @@ class ConfigHandler(BaseHandler):
                     def call_reboot():
                         powerctl.reboot()
                     
-                    ioloop = IOLoop.instance()
-                    ioloop.add_timeout(datetime.timedelta(seconds=2), call_reboot)
+                    io_loop = IOLoop.instance()
+                    io_loop.add_timeout(datetime.timedelta(seconds=2), call_reboot)
                     return self.finish({'reload': False, 'reboot': True, 'error': None})
                 
                 else:
@@ -1438,10 +1438,12 @@ class PowerHandler(BaseHandler):
             self.reboot()
     
     def shut_down(self):
-        IOLoop.instance().add_timeout(datetime.timedelta(seconds=2), powerctl.shut_down)
+        io_loop = IOLoop.instance()
+        io_loop.add_timeout(datetime.timedelta(seconds=2), powerctl.shut_down)
 
     def reboot(self):
-        IOLoop.instance().add_timeout(datetime.timedelta(seconds=2), powerctl.reboot)
+        io_loop = IOLoop.instance()
+        io_loop.add_timeout(datetime.timedelta(seconds=2), powerctl.reboot)
 
 
 class VersionHandler(BaseHandler):
index a275592b4d1e6d77db6890e034d7e517a5ba05a7..752387026240a2c2644fd8d9ab822ca4cf7a696e 100644 (file)
@@ -24,15 +24,15 @@ import logging
 import multiprocessing
 import os.path
 import re
+import signal
 import stat
 import StringIO
 import subprocess
 import time
-import tornado
 import zipfile
 
 from PIL import Image
-from tornado import ioloop
+from tornado.ioloop import IOLoop
 
 import config
 import settings
@@ -326,17 +326,22 @@ def list_media(camera_config, media_type, callback, prefix=None):
             media_list.append(parent_pipe.recv())
     
     def poll_process():
-        ioloop = tornado.ioloop.IOLoop.instance()
+        io_loop = IOLoop.instance()
         if process.is_alive(): # not finished yet
             now = datetime.datetime.now()
             delta = now - started
-            if delta.seconds < 120:
-                ioloop.add_timeout(datetime.timedelta(seconds=0.5), poll_process)
+            if delta.seconds < settings.LIST_MEDIA_TIMEOUT:
+                io_loop.add_timeout(datetime.timedelta(seconds=0.5), poll_process)
                 read_media_list()
             
-            else: # process did not finish within 2 minutes
+            else: # process did not finish in time
                 logging.error('timeout waiting for the media listing process to finish')
+                try:
+                    os.kill(process.pid, signal.SIGTERM)
                 
+                except:
+                    pass # nevermind
+                    
                 callback(None)
 
         else: # finished
@@ -430,15 +435,20 @@ def get_zipped_content(camera_config, media_type, group, callback):
     started = datetime.datetime.now()
 
     def poll_process():
-        ioloop = tornado.ioloop.IOLoop.instance()
+        io_loop = IOLoop.instance()
         if working.value:
             now = datetime.datetime.now()
             delta = now - started
             if delta.seconds < settings.ZIP_TIMEOUT:
-                ioloop.add_timeout(datetime.timedelta(seconds=0.5), poll_process)
+                io_loop.add_timeout(datetime.timedelta(seconds=0.5), poll_process)
 
-            else: # process did not finish within 2 minutes
+            else: # process did not finish in time
                 logging.error('timeout waiting for the zip process to finish')
+                try:
+                    os.kill(process.pid, signal.SIGTERM)
+                
+                except:
+                    pass # nevermind
 
                 callback(None)
 
@@ -492,17 +502,22 @@ def make_timelapse_movie(camera_config, framerate, interval, group):
             media_list.append(parent_pipe.recv())
         
     def poll_media_list_process():
-        ioloop = tornado.ioloop.IOLoop.instance()
+        io_loop = IOLoop.instance()
         if _timelapse_process.is_alive(): # not finished yet
             now = datetime.datetime.now()
             delta = now - started[0]
-            if delta.seconds < 300: # the subprocess has 5 minutes to complete its job
-                ioloop.add_timeout(datetime.timedelta(seconds=0.5), poll_media_list_process)
+            if delta.seconds < settings.TIMELAPSE_TIMEOUT: # the subprocess has limited time to complete its job
+                io_loop.add_timeout(datetime.timedelta(seconds=0.5), poll_media_list_process)
                 read_media_list()
 
-            else: # process did not finish within 2 minutes
+            else: # process did not finish in time
                 logging.error('timeout waiting for the media listing process to finish')
+                try:
+                    os.kill(_timelapse_process.pid, signal.SIGTERM)
                 
+                except:
+                    pass # nevermind
+
                 _timelapse_process.progress = -1
 
         else: # finished
@@ -573,9 +588,9 @@ def make_timelapse_movie(camera_config, framerate, interval, group):
         global _timelapse_process
         global _timelapse_data
         
-        ioloop = tornado.ioloop.IOLoop.instance()
+        io_loop = IOLoop.instance()
         if _timelapse_process.poll() is None: # not finished yet
-            ioloop.add_timeout(datetime.timedelta(seconds=0.5), functools.partial(poll_movie_process, pictures))
+            io_loop.add_timeout(datetime.timedelta(seconds=0.5), functools.partial(poll_movie_process, pictures))
 
             try:
                 output = _timelapse_process.stdout.read()
@@ -800,6 +815,8 @@ def set_prepared_cache(data):
             logging.warn('key "%s" was still present in the prepared cache, removed' % key)
 
     timeout = 3600 # the user has 1 hour to download the file after creation
-    ioloop.IOLoop.instance().add_timeout(datetime.timedelta(seconds=timeout), clear)
+    
+    io_loop = IOLoop.instance()
+    io_loop.add_timeout(datetime.timedelta(seconds=timeout), clear)
 
     return key
index 5ba7f57842a217850f7f20d20af926b4ee959a9c..b785b3b78e07f00950108b718dfc7ee7b38aa54b 100755 (executable)
@@ -22,8 +22,6 @@ import os.path
 import pipes
 import sys
 
-from tornado.httpclient import AsyncHTTPClient
-
 # make sure motioneye is on python path
 sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 
@@ -178,6 +176,8 @@ def configure_logging(cmd, log_to_file=False):
 
 
 def configure_tornado():
+    from tornado.httpclient import AsyncHTTPClient
+
     AsyncHTTPClient.configure('tornado.curl_httpclient.CurlAsyncHTTPClient', max_clients=16)
 
 
@@ -242,21 +242,21 @@ def main():
     command = sys.argv[1]
     arg_parser = make_arg_parser(command)
 
-    import relayevent
-    import sendmail
-    import server
-    import webhook
 
     if command in ('startserver', 'stopserver'):
+        import server
         server.main(arg_parser, sys.argv[2:], command[:-6])
 
     elif command == 'sendmail':
+        import sendmail
         sendmail.main(arg_parser, sys.argv[2:])
     
     elif command == 'relayevent':
+        import relayevent
         relayevent.main(arg_parser, sys.argv[2:])
 
     elif command == 'webhook':
+        import webhook
         webhook.main(arg_parser, sys.argv[2:])
 
     else:
index 933df2dde4f154d4af71002bc7d757e65ce07fc1..949e599053c1aca6bc2feafc85161f997140a2f1 100644 (file)
@@ -22,7 +22,8 @@ import re
 import socket
 import time
 
-from tornado import iostream, ioloop
+from tornado.ioloop import IOLoop
+from tornado.iostream import IOStream
 
 import config
 import motionctl
@@ -30,7 +31,7 @@ import settings
 import utils
 
 
-class MjpgClient(iostream.IOStream):
+class MjpgClient(IOStream):
     clients = {} # dictionary of clients indexed by camera id
     last_jpgs = {} # dictionary of jpg contents indexed by camera id
     last_jpg_moment = {} # dictionary of moments of the last received jpeg indexed by camera id
@@ -45,12 +46,12 @@ class MjpgClient(iostream.IOStream):
         self._auth_digest_state = {}
         
         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
-        iostream.IOStream.__init__(self, s)
+        IOStream.__init__(self, s)
         
         self.set_close_callback(self.on_close)
         
     def connect(self):
-        iostream.IOStream.connect(self, ('localhost', self._port), self._on_connect)
+        IOStream.connect(self, ('localhost', self._port), self._on_connect)
         MjpgClient.clients[self._camera_id] = self
         
         logging.debug('mjpg client for camera %(camera_id)s connecting on port %(port)s...' % {
@@ -212,7 +213,7 @@ class MjpgClient(iostream.IOStream):
 
 def start():
     # schedule the garbage collector
-    io_loop = ioloop.IOLoop.instance()
+    io_loop = IOLoop.instance()
     io_loop.add_timeout(datetime.timedelta(seconds=settings.MJPG_CLIENT_TIMEOUT), _garbage_collector)
 
 
@@ -258,7 +259,7 @@ def close_all(invalidate=False):
 def _garbage_collector():
     logging.debug('running garbage collector for mjpg clients...')
 
-    io_loop = ioloop.IOLoop.instance()
+    io_loop = IOLoop.instance()
     io_loop.add_timeout(datetime.timedelta(seconds=settings.MJPG_CLIENT_TIMEOUT), _garbage_collector)
 
     now = datetime.datetime.utcnow()
index 58e8266fa493da358d9736c739d2b01b865efc10..39265ca8b18d01a98721aa36dfdecb65df1b6a09 100644 (file)
@@ -23,16 +23,14 @@ import signal
 import subprocess
 import time
 
-from tornado import gen
-from tornado.httpclient import AsyncHTTPClient, HTTPRequest
 from tornado.ioloop import IOLoop
 
 import config
-import mjpgclient
 import powerctl
 import settings
 import utils
 
+_MOTION_CONTROL_TIMEOUT = 5
 
 _started = False
 _motion_binary_cache = None
@@ -73,8 +71,11 @@ def find_motion():
 
 
 def start(deferred=False):
+    import mjpgclient
+    
     if deferred:
-        return IOLoop.instance().add_callback(start, deferred=False)
+        io_loop = IOLoop.instance()
+        io_loop.add_callback(start, deferred=False)
 
     global _started
     
@@ -136,6 +137,8 @@ def start(deferred=False):
 
 
 def stop(invalidate=False):
+    import mjpgclient
+    
     global _started
     
     _started = False
@@ -202,39 +205,37 @@ def started():
     return _started
 
 
-@gen.coroutine
-def get_motion_detection(camera_id):
+def get_motion_detection(camera_id, callback):
+    from tornado.httpclient import HTTPRequest, AsyncHTTPClient
+    
     thread_id = camera_id_to_thread_id(camera_id)
     if thread_id is None:
-        logging.error('could not find thread id for camera with id %s' % camera_id)
-        return
+        error = 'could not find thread id for camera with id %s' % camera_id
+        logging.error(error)
+        return callback(error=error)
 
     url = 'http://127.0.0.1:7999/%(id)s/detection/status' % {'id': thread_id}
     
-    request = HTTPRequest(url, connect_timeout=5, request_timeout=5)
-    http_client = AsyncHTTPClient()
-    try:
-        response = yield http_client.fetch(request)
+    def on_response(response):
         if response.error:
-            raise response.error
+            return callback(error=utils.pretty_http_error())
 
-    except Exception as e:
-        logging.error('failed to get motion detection status for camera with id %(id)s: %(msg)s' % {
-                'id': camera_id,
-                'msg': unicode(e)})
-        return
+        enabled = bool(response.body.lower().count('active'))
+        
+        logging.debug('motion detection is %(what)s for camera with id %(id)s' % {
+                'what': ['disabled', 'enabled'][enabled],
+                'id': camera_id})
 
-    enabled = bool(response.body.lower().count('active'))
-    
-    logging.debug('motion detection is %(what)s for camera with id %(id)s' % {
-            'what': ['disabled', 'enabled'][enabled],
-            'id': camera_id})
+        callback(enabled)
 
-    raise gen.Return(enabled)
+    request = HTTPRequest(url, connect_timeout=_MOTION_CONTROL_TIMEOUT, request_timeout=_MOTION_CONTROL_TIMEOUT)
+    http_client = AsyncHTTPClient()
+    http_client.fetch(request, callback=on_response)
 
 
 def set_motion_detection(camera_id, enabled):
+    from tornado.httpclient import HTTPRequest, AsyncHTTPClient
+    
     thread_id = camera_id_to_thread_id(camera_id)
     if thread_id is None:
         return logging.error('could not find thread id for camera with id %s' % camera_id)
@@ -262,7 +263,7 @@ def set_motion_detection(camera_id, enabled):
                     'what': ['disabled', 'enabled'][enabled],
                     'id': camera_id})
 
-    request = HTTPRequest(url, connect_timeout=4, request_timeout=4)
+    request = HTTPRequest(url, connect_timeout=_MOTION_CONTROL_TIMEOUT, request_timeout=_MOTION_CONTROL_TIMEOUT)
     http_client = AsyncHTTPClient()
     http_client.fetch(request, on_response)
 
index 5c64556c07bac775f826831a1920deb86107419e..19c3a697794513a4d9e5c1e3c907ceda01b8f961 100644 (file)
@@ -19,6 +19,7 @@ import datetime
 import logging
 import os
 import re
+import signal
 import smtplib
 import socket
 import time
@@ -27,6 +28,7 @@ from email import Encoders
 from email.mime.text import MIMEText
 from email.MIMEMultipart import MIMEMultipart
 from email.MIMEBase import MIMEBase
+
 from tornado.ioloop import IOLoop
 
 import settings
@@ -81,16 +83,21 @@ def send_mail(server, port, account, password, tls, to, subject, message, files)
 def make_message(subject, message, camera_id, moment, timespan, callback):
     camera_config = config.get_camera(camera_id)
     
+    # we must start the IO loop for the media list subprocess polling
+    io_loop = IOLoop.instance()
+
     def on_media_files(media_files):
-        logging.debug('got media files')
+        io_loop.stop()
         
         timestamp = time.mktime(moment.timetuple())
 
-        media_files = [m for m in media_files if abs(m['timestamp'] - timestamp) < timespan] # filter out non-recent media files
-        media_files.sort(key=lambda m: m['timestamp'], reverse=True)
-        media_files = [os.path.join(camera_config['target_dir'], re.sub('^/', '', m['path'])) for m in media_files]
+        if media_files:
+            logging.debug('got media files')
+            media_files = [m for m in media_files if abs(m['timestamp'] - timestamp) < timespan] # filter out non-recent media files
+            media_files.sort(key=lambda m: m['timestamp'], reverse=True)
+            media_files = [os.path.join(camera_config['target_dir'], re.sub('^/', '', m['path'])) for m in media_files]
         
-        logging.debug('selected %d pictures' % len(media_files))
+            logging.debug('selected %d pictures' % len(media_files))
 
         format_dict = {
             'camera': camera_config['@name'],
@@ -99,8 +106,8 @@ def make_message(subject, message, camera_id, moment, timespan, callback):
         }
         
         if settings.LOCAL_TIME_FILE:
-            format_dict['timezone'] = tzctl._get_time_zone()
-        
+            format_dict['timezone'] = tzctl.get_time_zone()
+
         else:
             format_dict['timezone'] = 'local time'
     
@@ -115,11 +122,14 @@ def make_message(subject, message, camera_id, moment, timespan, callback):
 
     if not timespan:
         return on_media_files([])
-
-    logging.debug('creating email message')
-
+    
+    logging.debug('waiting for pictures to be taken')
     time.sleep(timespan) # give motion some time to create motion pictures
+    
+    logging.debug('creating email message')
     mediafiles.list_media(camera_config, media_type='picture', callback=on_media_files)
+    
+    io_loop.start()
 
 
 def parse_options(parser, args):
@@ -140,10 +150,14 @@ def parse_options(parser, args):
 def main(parser, args):
     import meyectl
     
+    # the motion daemon overrides SIGCHLD,
+    # so we must restore it here,
+    # or otherwise media listing won't work
+    signal.signal(signal.SIGCHLD,signal.SIG_DFL)
+
     options = parse_options(parser, args)
     
     meyectl.configure_logging('sendmail', options.log_to_file)
-    meyectl.configure_tornado()
 
     logging.debug('hello!')
 
@@ -154,6 +168,10 @@ def main(parser, args):
     subject = subjects.get(options.msg_id)
     options.moment = datetime.datetime.strptime(options.moment, '%Y-%m-%dT%H:%M:%S')
     
+    # do not wait too long for media list,
+    # email notifications are critical
+    settings.LIST_MEDIA_TIMEOUT = 10
+    
     logging.debug('server = %s' % options.server)
     logging.debug('port = %s' % options.port)
     logging.debug('account = %s' % options.account)
@@ -170,25 +188,15 @@ def main(parser, args):
     to = [t.strip() for t in re.split('[,;| ]', options.to)]
     to = [t for t in to if t]
 
-    io_loop = IOLoop.instance()
-    
     def on_message(subject, message, files):
         try:
             send_mail(options.server, options.port, options.account, options.password,
-                    options.tls, to, subject, message, files)
+                    options.tls, to, subject, message, files or [])
             logging.info('email sent')
 
         except Exception as e:
             logging.error('failed to send mail: %s' % e, exc_info=True)
 
-        io_loop.stop()
-    
-    def ioloop_timeout():
-        io_loop.stop()
+        logging.debug('bye!')
     
     make_message(subject, message, options.camera_id, options.moment, options.timespan, on_message)
-
-    io_loop.add_timeout(datetime.timedelta(seconds=settings.SMTP_TIMEOUT), ioloop_timeout)
-    io_loop.start()
-
-    logging.debug('bye!')
index 19b8db7df31908a8526312424c23f8932e69538a..e2a3eabe6a2c105a99f363dc7a5010867e9f221d 100644 (file)
@@ -24,8 +24,8 @@ import signal
 import sys
 import time
 
-from tornado.web import Application
 from tornado.ioloop import IOLoop
+from tornado.web import Application
 
 import handlers
 import settings
@@ -284,15 +284,14 @@ def test_requirements():
 
         
 def start_motion():
-    import tornado.ioloop
     import config
     import motionctl
 
-    ioloop = tornado.ioloop.IOLoop.instance()
+    io_loop = IOLoop.instance()
     
     # add a motion running checker
     def checker():
-        if ioloop._stopped:
+        if io_loop._stopped:
             return
             
         if not motionctl.running() and motionctl.started() and config.get_enabled_local_motion_cameras():
@@ -304,11 +303,11 @@ def start_motion():
                 logging.error('failed to start motion: %(msg)s' % {
                         'msg': unicode(e)}, exc_info=True)
 
-        ioloop.add_timeout(datetime.timedelta(seconds=settings.MOTION_CHECK_INTERVAL), checker)
+        io_loop.add_timeout(datetime.timedelta(seconds=settings.MOTION_CHECK_INTERVAL), checker)
     
     motionctl.start()
         
-    ioloop.add_timeout(datetime.timedelta(seconds=settings.MOTION_CHECK_INTERVAL), checker)
+    io_loop.add_timeout(datetime.timedelta(seconds=settings.MOTION_CHECK_INTERVAL), checker)
 
 
 def parse_options(parser, args):
@@ -367,7 +366,8 @@ def run():
     application.listen(settings.PORT, settings.LISTEN)
     logging.info('server started')
     
-    IOLoop.instance().start()
+    io_loop = IOLoop.instance()
+    io_loop.start()
 
     logging.info('server stopped')
     
index 8f593a442259a9805342e128bafa24f1f8a96763..5f3fb180d0d8cbbdf3e6ff66f6e61566048ec322 100644 (file)
@@ -111,8 +111,14 @@ ENABLE_UPDATE = False
 # timeout in seconds to use when talking to the SMTP server
 SMTP_TIMEOUT = 60
 
+# timeout in seconds to wait media files list
+LIST_MEDIA_TIMEOUT = 120
+
 # timeout in seconds to wait for zip file creation
 ZIP_TIMEOUT = 500
 
+# timeout in seconds to wait for timelapse creation
+TIMELAPSE_TIMEOUT = 500
+
 # enable adding and removing cameras from UI
 ADD_REMOVE_CAMERAS = True
index 936c04b9cc21d21ad28c80bb050d10e511e61dac..75f1aa75edc354be559d2910937bbc2285fdbb57 100644 (file)
@@ -22,14 +22,14 @@ import re
 import subprocess
 import time
 
-from tornado import ioloop
+from tornado.ioloop import IOLoop
 
 import config
 import settings
 
 
 def start():
-    io_loop = ioloop.IOLoop.instance()
+    io_loop = IOLoop.instance()
     io_loop.add_timeout(datetime.timedelta(seconds=settings.MOUNT_CHECK_INTERVAL), _check_mounts)
 
 
@@ -229,6 +229,6 @@ def _check_mounts():
     if start:
         motionctl.start()
         
-    io_loop = ioloop.IOLoop.instance()
+    io_loop = IOLoop.instance()
     io_loop.add_timeout(datetime.timedelta(seconds=settings.MOUNT_CHECK_INTERVAL), _check_mounts)
 
index e100f0f3cbd3af6608912d9f77270402add9d7aa..0edd3a8ec9b0a32bac171e76d6d7b6ea49ee2931 100644 (file)
@@ -20,7 +20,8 @@ import logging
 import multiprocessing
 import os
 import signal
-import tornado
+
+from tornado.ioloop import IOLoop
 
 import cleanup
 import mediafiles
@@ -35,8 +36,8 @@ def start():
         return
 
     # schedule the first call a bit later to improve performance at startup
-    ioloop = tornado.ioloop.IOLoop.instance()
-    ioloop.add_timeout(datetime.timedelta(seconds=min(settings.THUMBNAILER_INTERVAL, 30)), _run_process)
+    io_loop = IOLoop.instance()
+    io_loop.add_timeout(datetime.timedelta(seconds=min(settings.THUMBNAILER_INTERVAL, 30)), _run_process)
 
 
 def stop():
@@ -64,8 +65,8 @@ def _run_process():
     global _process
     
     # schedule the next call
-    ioloop = tornado.ioloop.IOLoop.instance()
-    ioloop.add_timeout(datetime.timedelta(seconds=settings.THUMBNAILER_INTERVAL), _run_process)
+    io_loop = IOLoop.instance()
+    io_loop.add_timeout(datetime.timedelta(seconds=settings.THUMBNAILER_INTERVAL), _run_process)
 
     if not running() and not cleanup.running(): # check that the previous process has finished and that cleanup is not running
         logging.debug('running thumbnailer process...')
index ff36ab78b7b7cf54dbad558289b1480534aebc0d..d06ed23e6cfa19f9da2dc446e40a51eefcc12dd0 100644 (file)
@@ -27,6 +27,10 @@ from config import additional_config
 LOCAL_TIME_FILE = settings.LOCAL_TIME_FILE  # @UndefinedVariable
 
 
+def get_time_zone():
+    return _get_time_zone_symlink() or _get_time_zone_md5() or 'UTC'
+
+
 def _get_time_zone_symlink():
     file = settings.LOCAL_TIME_FILE
     if not file:
@@ -86,10 +90,6 @@ def _get_time_zone_md5():
     return time_zone
 
 
-def _get_time_zone():
-    return _get_time_zone_symlink() or _get_time_zone_md5() or 'UTC'
-
-
 def _set_time_zone(time_zone):
     time_zone = time_zone or 'UTC'
 
@@ -134,6 +134,6 @@ def timeZone():
         'section': 'general',
         'advanced': True,
         'reboot': True,
-        'get': _get_time_zone,
+        'get': get_time_zone,
         'set': _set_time_zone
     }
index f88d2de3d4b5bf9e6f9b689708e97c1d05832185..2b892259649eb985d3ede2d3806ec90f8b211ed3 100644 (file)
@@ -33,7 +33,6 @@ from tornado.ioloop import IOLoop
 
 import settings
 
-
 try:
     from collections import OrderedDict  # @UnusedImport
 
@@ -427,6 +426,8 @@ def test_rtsp_url(data, callback):
     called = [False]
     timeout = [None]
     stream = None
+    
+    io_loop = IOLoop.instance()
 
     def connect():
         logging.debug('testing rtsp netcam at %s' % url)
@@ -437,13 +438,13 @@ def test_rtsp_url(data, callback):
         stream.set_close_callback(on_close)
         stream.connect((data['host'], int(data['port'])), on_connect)
         
-        timeout[0] = IOLoop.instance().add_timeout(datetime.timedelta(seconds=settings.MJPG_CLIENT_TIMEOUT),
+        timeout[0] = io_loop.add_timeout(datetime.timedelta(seconds=settings.MJPG_CLIENT_TIMEOUT),
                 functools.partial(on_connect, _timeout=True))
         
         return stream
     
     def on_connect(_timeout=False):
-        IOLoop.instance().remove_timeout(timeout[0])
+        io_loop.remove_timeout(timeout[0])
         
         if _timeout:
             return handle_error('timeout connecting to rtsp netcam')
@@ -468,10 +469,10 @@ def test_rtsp_url(data, callback):
             return
 
         stream.read_until_regex('RTSP/1.0 \d+ ', on_rtsp)
-        timeout[0] = IOLoop.instance().add_timeout(datetime.timedelta(seconds=settings.MJPG_CLIENT_TIMEOUT), on_rtsp)
+        timeout[0] = io_loop.add_timeout(datetime.timedelta(seconds=settings.MJPG_CLIENT_TIMEOUT), on_rtsp)
 
     def on_rtsp(data):
-        IOLoop.instance().remove_timeout(timeout[0])
+        io_loop.remove_timeout(timeout[0])
 
         if data:
             if data.endswith('200 '):
@@ -488,10 +489,10 @@ def test_rtsp_url(data, callback):
             return
 
         stream.read_until_regex('Server: .*', on_server)
-        timeout[0] = IOLoop.instance().add_timeout(datetime.timedelta(seconds=1), on_server)
+        timeout[0] = io_loop.add_timeout(datetime.timedelta(seconds=1), on_server)
 
     def on_server(data=None):
-        IOLoop.instance().remove_timeout(timeout[0])
+        io_loop.remove_timeout(timeout[0])
 
         if data:
             identifier = re.findall('Server: (.*)', data)[0].strip()
index 59490d0f66228cbb18ac193418c1744ddabe4d7a..04b9f5b7a70da52f068beccf742582d57fb0e48c 100644 (file)
 # along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 
 import datetime
+import functools
 import logging
 
-from tornado import ioloop, gen
+from tornado.ioloop import IOLoop
 
 import config
 import motionctl
@@ -26,7 +27,7 @@ import utils
 
 
 def start():
-    io_loop = ioloop.IOLoop.instance()
+    io_loop = IOLoop.instance()
     io_loop.add_timeout(datetime.timedelta(seconds=1), _check_ws)
 
 
@@ -70,15 +71,32 @@ def _during_working_schedule(now, working_schedule):
     return True
 
 
-@gen.coroutine
 def _check_ws():
     # schedule the next call
-    io_loop = ioloop.IOLoop.instance()
+    io_loop = IOLoop.instance()
     io_loop.add_timeout(datetime.timedelta(seconds=10), _check_ws)
 
     if not motionctl.running():
         return
     
+    def on_motion_detection_status(camera_id, must_be_enabled, working_schedule_type, enabled=None, error=None):
+        if error: # could not detect current status
+            return logging.warn('skipping motion detection status update for camera with id %(id)s' % {'id': camera_id})
+            
+        if enabled and not must_be_enabled:
+            logging.debug('must disable motion detection for camera with id %(id)s (%(what)s working schedule)' % {
+                    'id': camera_id,
+                    'what': working_schedule_type})
+            
+            motionctl.set_motion_detection(camera_id, False)
+
+        elif not enabled and must_be_enabled:
+            logging.debug('must enable motion detection for camera with id %(id)s (%(what)s working schedule)' % {
+                    'id': camera_id,
+                    'what': working_schedule_type})
+            
+            motionctl.set_motion_detection(camera_id, True)
+    
     now = datetime.datetime.now()
     for camera_id in config.get_camera_ids():
         camera_config = config.get_camera(camera_id)
@@ -98,21 +116,5 @@ def _check_ws():
         now_during = _during_working_schedule(now, working_schedule)
         must_be_enabled = (now_during and working_schedule_type == 'during') or (not now_during and working_schedule_type == 'outside')
         
-        currently_enabled = yield motionctl.get_motion_detection(camera_id)
-        if currently_enabled is None: # could not detect current status
-            logging.warn('skipping motion detection status update for camera with id %(id)s' % {'id': camera_id})
-            continue
-            
-        if currently_enabled and not must_be_enabled:
-            logging.debug('must disable motion detection for camera with id %(id)s (%(what)s working schedule)' % {
-                    'id': camera_id,
-                    'what': working_schedule_type})
-            
-            motionctl.set_motion_detection(camera_id, False)
-
-        elif not currently_enabled and must_be_enabled:
-            logging.debug('must enable motion detection for camera with id %(id)s (%(what)s working schedule)' % {
-                    'id': camera_id,
-                    'what': working_schedule_type})
-            
-            motionctl.set_motion_detection(camera_id, True)
+        motionctl.get_motion_detection(camera_id, functools.partial(
+                on_motion_detection_status, camera_id, must_be_enabled, working_schedule_type))