From ef2173e091ce22ebad81b8d6e8b8cbfed8a35fc8 Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Thu, 10 Jul 2014 16:47:00 +0300 Subject: [PATCH] initial work on timezone support --- motioneye.py | 9 ++-- settings_default.py | 27 ++++++----- src/config.py | 19 ++++++++ src/handlers.py | 6 +++ src/tzctl.py | 110 ++++++++++++++++++++++++++++++++++++++++++++ templates/main.html | 11 +++++ 6 files changed, 166 insertions(+), 16 deletions(-) create mode 100644 src/tzctl.py diff --git a/motioneye.py b/motioneye.py index 0b13270..15e50ee 100755 --- a/motioneye.py +++ b/motioneye.py @@ -47,10 +47,6 @@ def _configure_settings(): set_default_setting('LOG_LEVEL', logging.INFO) set_default_setting('LISTEN', '0.0.0.0') set_default_setting('PORT', 8765) - set_default_setting('WPA_SUPPLICANT_CONF', None) - set_default_setting('SMB_SHARES', False) - set_default_setting('SMB_MOUNT_ROOT', '/media') - set_default_setting('ENABLE_REBOOT', False) set_default_setting('MOUNT_CHECK_INTERVAL', 300) set_default_setting('MOTION_CHECK_INTERVAL', 10) set_default_setting('CLEANUP_INTERVAL', 43200) @@ -59,6 +55,11 @@ def _configure_settings(): set_default_setting('MJPG_CLIENT_TIMEOUT', 10) set_default_setting('PICTURE_CACHE_SIZE', 8) set_default_setting('PICTURE_CACHE_LIFETIME', 60) + set_default_setting('SMB_SHARES', False) + set_default_setting('SMB_MOUNT_ROOT', '/media') + set_default_setting('WPA_SUPPLICANT_CONF', None) + set_default_setting('LOCAL_TIME_FILE', None) + set_default_setting('ENABLE_REBOOT', False) length = len(sys.argv) - 1 for i in xrange(length): diff --git a/settings_default.py b/settings_default.py index 283447c..2faf74c 100644 --- a/settings_default.py +++ b/settings_default.py @@ -29,18 +29,6 @@ LISTEN = '0.0.0.0' # change the port according to your requirements/restrictions PORT = 8765 -# path to a wpa_supplicant.conf file if wifi settings UI is desired (requires root) -WPA_SUPPLICANT_CONF = None - -# enable SMB shares (requires root) -SMB_SHARES = False - -# the directory where the SMB mounts will be created -SMB_MOUNT_ROOT = '/media' - -# enables rebooting after changing system settings (such as wifi settings or system updates) -ENABLE_REBOOT = False - # interval in seconds at which motionEye checks the SMB mounts MOUNT_CHECK_INTERVAL = 300 @@ -64,3 +52,18 @@ PICTURE_CACHE_SIZE = 8 # the number of seconds that a cached picture is valid PICTURE_CACHE_LIFETIME = 60 + +# enable SMB shares (requires root) +SMB_SHARES = False + +# the directory where the SMB mounts will be created +SMB_MOUNT_ROOT = '/media' + +# path to a wpa_supplicant.conf file if wifi settings UI is desired +WPA_SUPPLICANT_CONF = None + +# path to a localtime file if time zone settings UI is desired +LOCAL_TIME_FILE = None + +# enables rebooting after changing system settings (such as wifi settings or system updates) +ENABLE_REBOOT = False diff --git a/src/config.py b/src/config.py index 85ddad2..5e042e7 100644 --- a/src/config.py +++ b/src/config.py @@ -26,6 +26,7 @@ import diskctl import motionctl import settings import smbctl +import tzctl import update import utils import v4l2ctl @@ -89,6 +90,9 @@ def get_main(as_lines=False): if settings.WPA_SUPPLICANT_CONF: _get_wifi_settings(main_config) + if settings.LOCAL_TIME_FILE: + _get_localtime_settings(main_config) + _set_default_motion(main_config) _main_config_cache = main_config @@ -105,6 +109,9 @@ def set_main(main_config): if settings.WPA_SUPPLICANT_CONF: _set_wifi_settings(main_config) + if settings.LOCAL_TIME_FILE: + _set_localtime_settings(main_config) + config_file_path = os.path.join(settings.CONF_PATH, _MAIN_CONFIG_FILE_NAME) # read the actual configuration from file @@ -1102,6 +1109,7 @@ def _set_default_motion(data): data.setdefault('@admin_password', '') data.setdefault('@normal_username', 'user') data.setdefault('@normal_password', '') + data.setdefault('@timezone', 'UTC') data.setdefault('@wifi_enabled', False) data.setdefault('@wifi_name', '') @@ -1334,3 +1342,14 @@ def _set_wifi_settings(data): conf_file.write(line) conf_file.close() + + +def _get_localtime_settings(data): + time_zone = tzctl.get_time_zone() + data['@time_zone'] = time_zone + + +def _set_localtime_settings(data): + time_zone = data.pop('@time_zone') + if time_zone: + tzctl.set_time_zone(time_zone) diff --git a/src/handlers.py b/src/handlers.py index ff51fc2..dbfb230 100644 --- a/src/handlers.py +++ b/src/handlers.py @@ -20,6 +20,7 @@ import datetime import json import logging import os +import pytz import socket from tornado.web import RequestHandler, HTTPError, asynchronous @@ -145,8 +146,13 @@ class NotFoundHandler(BaseHandler): class MainHandler(BaseHandler): @BaseHandler.auth() def get(self): + timezones = [] + if settings.LOCAL_TIME_FILE: + timezones = pytz.common_timezones + self.render('main.html', wpa_supplicant=settings.WPA_SUPPLICANT_CONF, + timezones=timezones, hostname=socket.gethostname()) diff --git a/src/tzctl.py b/src/tzctl.py new file mode 100644 index 0000000..04e97af --- /dev/null +++ b/src/tzctl.py @@ -0,0 +1,110 @@ + +# Copyright (c) 2013 Calin Crisan +# This file is part of motionEye. +# +# motionEye is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import hashlib +import logging +import os +import settings +import subprocess + + +def _get_time_zone_symlink(): + file = settings.LOCAL_TIME_FILE + + for i in xrange(8): # recursively follow the symlinks @UnusedVariable + try: + file = os.readlink(file) + + except OSError: + break + + if file and file.startswith('/usr/share/zoneinfo/'): + file = file[21:] + + else: + file = None + + time_zone = file or None + if time_zone: + logging.debug('found time zone by symlink method: %s' % time_zone) + + return time_zone + + +def _get_time_zone_md5(): + try: + output = subprocess.check_output('cd /usr/share/zoneinfo; find * -type f | xargs md5sum', shell=True) + + except Exception as e: + logging.error('getting md5 of zoneinfo files failed: %s' % e) + + return None + + lines = [l for l in output.split('\n') if l] + lines = [l.split(None, 1) for l in lines] + time_zone_by_md5 = dict(lines) + + try: + with open(settings.LOCAL_TIME_FILE, 'r') as f: + data = f.read() + + except Exception as e: + logging.error('failed to read local time file: %s' % e) + + return None + + md5 = hashlib.md5(data).hexdigest() + time_zone = time_zone_by_md5.get(md5) + + if time_zone: + logging.debug('found time zone by md5 method: %s' % time_zone) + + return time_zone + + +def get_time_zone(): + time_zone = _get_time_zone_symlink() or _get_time_zone_md5() + if not time_zone: + logging.error('could not find local time zone') + + return time_zone + + +def set_time_zone(time_zone): + zoneinfo_file = '/usr/share/zoneinfo/' + time_zone + if not os.path.exists(zoneinfo_file): + logging.error('%s file does not exist' % zoneinfo_file) + + return False + + logging.debug('linking "%s" to "%s"...' % (settings.LOCAL_TIME_FILE, zoneinfo_file)) + + try: + os.remove(settings.LOCAL_TIME_FILE) + + except: + pass # nevermind + + try: + os.symlink(zoneinfo_file, settings.LOCAL_TIME_FILE) + + return True + + except Exception as e: + logging.error('failed to link "%s" to "%s": %s' % (settings.LOCAL_TIME_FILE, zoneinfo_file, e)) + + return False diff --git a/templates/main.html b/templates/main.html index f414881..b9390cd 100644 --- a/templates/main.html +++ b/templates/main.html @@ -63,6 +63,17 @@ ? + + Time Zone + + + + ? +
-- 2.39.5