From: Calin Crisan Date: Sat, 28 Sep 2013 11:33:21 +0000 (+0300) Subject: config ui is now linked with the config files X-Git-Url: http://www.vanbest.org/gitweb/?a=commitdiff_plain;h=84c85c69e46b086f070be8ae7dcf071468a9a69c;p=motioneye-debian config ui is now linked with the config files --- diff --git a/doc/todo.txt b/doc/todo.txt index 4121850..9fd65dc 100644 --- a/doc/todo.txt +++ b/doc/todo.txt @@ -1,5 +1,6 @@ +-> add complete js validation -> group @config rules to top --> notification: multiple email addresses and phone numbers +-> notification: multiple email addresses -> what do we do with working schedule -> browser compatibility test -> hint text next to section titles diff --git a/motioneye.py b/motioneye.py index eeec7a9..f3414f2 100644 --- a/motioneye.py +++ b/motioneye.py @@ -46,7 +46,7 @@ if __name__ == '__main__': # import config # main_config = config.get_main() -# #config.add_camera('v4l2:///dev/video0') +# config.add_camera('v4l2:///dev/video0') # #data = config.get_camera(1) # #data['@enabled'] = True # #config.set_camera(1, data) diff --git a/src/config.py b/src/config.py index 61f945c..9d67eab 100644 --- a/src/config.py +++ b/src/config.py @@ -294,238 +294,6 @@ def rem_camera(camera_id): raise -def camera_ui_to_dict(camera_id, ui): - video_device = ui.get('device', '') - if video_device.count('://'): - video_device = video_device.split('://')[-1] - - data = { - # device - '@name': ui.get('name', ''), - '@enabled': ui.get('enabled', False), - 'videodevice': video_device, - 'lightswitch': int(ui.get('light_switch_detect', False) * 5), - 'auto_brightness': ui.get('auto_brightness', False), - 'brightness': int(int(ui.get('brightness', 0)) * 2.55), - 'contrast': int(int(ui.get('contrast', 0)) * 2.55), - 'saturation': int(int(ui.get('saturation', 0)) * 2.55), - 'hue': int(int(ui.get('hue', 0))), - 'width': int(ui.get('resolution', '352x288').split('x')[0]), - 'height': int(ui.get('resolution', '352x288').split('x')[1]), - 'framerate': int(ui.get('framerate', 1)), - - # file storage - '@storage_device': ui.get('storage_device', 'local-disk'), - '@network_server': ui.get('network_server', ''), - '@network_share_name': ui.get('network_share_name', ''), - '@network_username': ui.get('network_username', ''), - '@network_password': ui.get('network_password', ''), - 'target_dir': ui.get('root_directory', '/'), - - # text overlay - 'text_left': '', - 'text_right': '', - - # streaming - 'webcam_localhost': not ui.get('video_streaming', True), - 'webcam_port': int(ui.get('streaming_port', 8080)), - 'webcam_maxrate': int(ui.get('streaming_framerate', 1)), - 'webcam_quality': max(1, int(ui.get('streaming_quality', 50))), - - # still images - 'output_normal': False, - 'output_all': False, - 'output_motion': False, - 'snapshot_interval': 0, - 'jpeg_filename': '', - 'snapshot_filename': '', - # TODO preserve images - - # movies - 'ffmpeg_variable_bitrate': 0, - 'ffmpeg_video_codec': 'mpeg4', - 'ffmpeg_cap_new': True, - 'movie_filename': '', - # TODO preserve movies - - # motion detection - 'text_changes': ui.get('show_frame_changes', False), - 'locate': ui.get('show_frame_changes', False), - 'threshold': ui.get('frame_change_threshold', 1500), - 'noise_tune': ui.get('auto_noise_detect', True), - 'noise_level': max(1, int(int(ui.get('noise_level', 8)) * 2.55)), - 'gap': int(ui.get('gap', 60)), - 'pre_capture': int(ui.get('pre_capture', 0)), - 'post_capture': int(ui.get('post_capture', 0)), - - # TODO notifications - } - - if ui.get('text_overlay', False): - left_text = ui.get('left_text', 'camera-name') - if left_text == 'camera-name': - data['text_left'] = ui.get('name') - - elif left_text == 'timestamp': - data['text_left'] = '%Y-%m-%d\n%T' - - else: - data['text_left'] = ui.get('custom_left_text', '') - - right_text = ui.get('right_text', 'timestamp') - if right_text == 'camera-name': - data['text_right'] = ui.get('name') - - elif right_text == 'timestamp': - data['text_right'] = '%Y-%m-%d\n%T' - - else: - data['text_right'] = ui.get('custom_right_text', '') - - if ui.get('still_images', False): - capture_mode = ui.get('capture_mode', 'motion-triggered') - if capture_mode == 'motion-triggered': - data['output_normal'] = True - data['jpeg_filename'] = ui.get('image_file_name', '%Y-%m-%d-%H-%M-%S-%q') - - elif capture_mode == 'interval-snapshots': - data['snapshot_interval'] = int(ui.get('snapshot_interval'), 300) - data['snapshot_filename'] = ui.get('image_file_name', '%Y-%m-%d-%H-%M-%S-%q') - - elif capture_mode == 'all-frames': - data['output_all'] = True - data['jpeg_filename'] = ui.get('image_file_name', '%Y-%m-%d-%H-%M-%S') - - data['quality'] = max(1, int(ui.get('image_quality', 75))) - - if ui.get('motion_movies', False): - data['ffmpeg_variable_bitrate'] = 2 + int((100 - int(ui.get('movie_quality', 50))) * 0.29) - data['movie_filename'] = ui.get('movie_file_name', '%Y-%m-%d-%H-%M-%S-%q') - - return data - - -def camera_dict_to_ui(camera_id, data): - # set the default options if not present - _set_default_motion_camera(data) - - ui = { - # device - 'name': data['@name'], - 'enabled': data['@enabled'], - 'device': 'v4l2://' + data['videodevice'], - 'light_switch_detect': data['lightswitch'] > 0, - 'auto_brightness': data['auto_brightness'], - 'brightness': int(int(data['brightness']) / 2.55), - 'contrast': int(int(data['contrast']) / 2.55), - 'saturation': int(int(data['saturation']) / 2.55), - 'hue': int(int(data['hue'])), - 'resolution': str(data['width']) + 'x' + str(data['height']), - 'framerate': int(data['framerate']), - - # file storage - 'storage_device': data['@storage_device'], - 'network_server': data['@network_server'], - 'network_share_name': data['@network_share_name'], - 'network_username': data['@network_username'], - 'network_password': data['@network_password'], - 'root_directory': data['target_dir'], - - # text overlay - 'text_overlay': False, - 'left_text': 'camera-name', - 'right_text': 'timestamp', - - # streaming - 'vudeo_streaming': not data['webcam_localhost'], - 'streaming_port': int(data['webcam_port']), - 'streaming_framerate': int(data['webcam_maxrate']), - 'streaming_quality': int(data['webcam_quality']), - - # still images - 'still_images': False, - 'capture_mode': 'motion-triggered', - 'image_file_name': '%Y-%m-%d-%H-%M-%S', - 'image_quality': 75, - # TODO preserve images - - # motion movies - 'motion_movies': False, - 'movie_quality': 50, - 'movie_file_name': '%Y-%m-%d-%H-%M-%S-%q', - # TODO preserve movies - - # motion detection - 'show_frame_changes': data.get('text_changes') or data.get('locate'), - 'frame_change_threshold': data['threshold'], - 'auto_noise_detect': data['noise_tune'], - 'noise_level': int(int(data['noise_level']) / 2.55), - 'gap': int(data['gap']), - 'pre_capture': int(data['pre_capture']), - 'post_capture': int(data['post_capture']), - - # TODO notifications - } - - text_left = data['text_left'] - text_right = data['text_right'] - if text_left or text_right: - ui['text_overlay'] = True - - if text_left == data['@name']: - ui['left_text'] = 'camera-name' - - elif text_left == '%Y-%m-%d\n%T': - ui['left_text'] = 'timestamp' - - else: - ui['left_text'] = 'custom-text' - ui['custom_left_text'] = text_left - - if text_right == data['@name']: - ui['right_text'] = 'camera-name' - - elif text_right == '%Y-%m-%d\n%T': - ui['right_text'] = 'timestamp' - - else: - ui['right_text'] = 'custom-text' - ui['custom_right_text'] = text_right - - output_all = data.get('output_all') - output_normal = data.get('output_normal') - jpeg_filename = data.get('jpeg_filename') - snapshot_interval = data.get('snapshot_interval') - snapshot_filename = data.get('snapshot_filename') - - if (((output_all or output_normal) and jpeg_filename) or - (snapshot_interval and snapshot_filename)): - - ui['still_images'] = True - - if output_all: - ui['capture_mode'] = 'all-frames' - ui['image_file_name'] = jpeg_filename - - elif data.get('snapshot_interval'): - ui['capture-mode'] = 'interval-snapshots' - ui['image_file_name'] = snapshot_filename - - elif data.get('output_normal'): - ui['capture-mode'] = 'motion-triggered' - ui['image_file_name'] = jpeg_filename - - ui['image_quality'] = ui.get('quality', 75) - - movie_filename = data.get('movie_filename') - if movie_filename: - ui['motion_movies'] = True - ui['movie_quality'] = int((max(2, data['ffmpeg_variable_bitrate']) - 2) / 0.29) - ui['movie_file_name'] = movie_filename - - return data - - def _value_to_python(value): value_lower = value.lower() if value_lower == 'off': @@ -693,6 +461,7 @@ def _set_default_motion(data): def _set_default_motion_camera(data): data.setdefault('@name', '') data.setdefault('@enabled', False) + data.setdefault('@proto', 'v4l2') data.setdefault('videodevice', '') data.setdefault('lightswitch', 0) data.setdefault('auto_brightness', False) @@ -715,6 +484,7 @@ def _set_default_motion_camera(data): data.setdefault('webcam_port', 8080) data.setdefault('webcam_maxrate', 1) data.setdefault('webcam_quality', 50) + data.setdefault('webcam_motion', False) data.setdefault('text_left', '') data.setdefault('text_right', '') @@ -735,6 +505,14 @@ def _set_default_motion_camera(data): data.setdefault('snapshot_interval', 0) data.setdefault('snapshot_filename', '') data.setdefault('quality', 75) + data.setdefault('@preserve_images', 0) data.setdefault('movie_filename', '') data.setdefault('ffmpeg_variable_bitrate', 14) + data.setdefault('@preserve_movies', 0) + + data.setdefault('@motion_notifications', False) + data.setdefault('@motion_notifications_emails', '') + + data.setdefault('@working_schedule', '') + diff --git a/src/handlers.py b/src/handlers.py index 655f958..b509729 100644 --- a/src/handlers.py +++ b/src/handlers.py @@ -63,11 +63,13 @@ class ConfigHandler(BaseHandler): if camera_id not in camera_ids: raise HTTPError(404, 'no such camera') - self.finish_json(config.get_camera(camera_id)) + ui_config = self._camera_dict_to_ui(config.get_camera(camera_id)) + self.finish_json(ui_config) else: logging.debug('getting main config') + # TODO _main_dict_to_ui self.finish_json(config.get_main()) def set_config(self, camera_id): @@ -85,7 +87,8 @@ class ConfigHandler(BaseHandler): camera_ids = config.get_camera_ids() if camera_id not in camera_ids: raise HTTPError(404, 'no such camera') - + + data = self._camera_ui_to_dict(data) config.set_camera(camera_id, data) else: @@ -99,6 +102,7 @@ class ConfigHandler(BaseHandler): raise + # TODO _main_ui_to_dict config.set_main(data) def list_cameras(self): @@ -127,6 +131,281 @@ class ConfigHandler(BaseHandler): config.rem_camera(camera_id) + def _camera_ui_to_dict(self, ui): + video_device = ui.get('device', '') + if video_device.count('://'): + video_device = video_device.split('://')[-1] + + if not ui.get('resolution'): # avoid errors for empty resolution setting + ui['resolution'] = '352x288' + + data = { + # device + '@name': ui.get('name', ''), + '@enabled': ui.get('enabled', False), + 'videodevice': video_device, + 'lightswitch': int(ui.get('light_switch_detect', False) * 5), + 'auto_brightness': ui.get('auto_brightness', False), + 'brightness': int(int(ui.get('brightness', 0)) * 2.55), + 'contrast': int(int(ui.get('contrast', 0)) * 2.55), + 'saturation': int(int(ui.get('saturation', 0)) * 2.55), + 'hue': int(int(ui.get('hue', 0))), + 'width': int(ui['resolution'].split('x')[0]), + 'height': int(ui['resolution'].split('x')[1]), + 'framerate': int(ui.get('framerate', 1)), + + # file storage + '@storage_device': ui.get('storage_device', 'local-disk'), + '@network_server': ui.get('network_server', ''), + '@network_share_name': ui.get('network_share_name', ''), + '@network_username': ui.get('network_username', ''), + '@network_password': ui.get('network_password', ''), + 'target_dir': ui.get('root_directory', '/'), + + # text overlay + 'text_left': '', + 'text_right': '', + + # streaming + 'webcam_localhost': not ui.get('video_streaming', True), + 'webcam_port': int(ui.get('streaming_port', 8080)), + 'webcam_maxrate': int(ui.get('streaming_framerate', 1)), + 'webcam_quality': max(1, int(ui.get('streaming_quality', 50))), + 'webcam_motion': ui.get('streaming_motion', False), + + # still images + 'output_normal': False, + 'output_all': False, + 'output_motion': False, + 'snapshot_interval': 0, + 'jpeg_filename': '', + 'snapshot_filename': '', + '@preserve_images': int(ui.get('preserve_images', 0)), + + # movies + 'ffmpeg_variable_bitrate': 0, + 'ffmpeg_video_codec': 'mpeg4', + 'ffmpeg_cap_new': True, + 'movie_filename': '', + '@preserve_movies': int(ui.get('preserve_movies', 0)), + + # motion detection + 'text_changes': ui.get('show_frame_changes', False), + 'locate': ui.get('show_frame_changes', False), + 'threshold': ui.get('frame_change_threshold', 1500), + 'noise_tune': ui.get('auto_noise_detect', True), + 'noise_level': max(1, int(int(ui.get('noise_level', 8)) * 2.55)), + 'gap': int(ui.get('gap', 60)), + 'pre_capture': int(ui.get('pre_capture', 0)), + 'post_capture': int(ui.get('post_capture', 0)), + + # motion notifications + '@motion_notifications': ui.get('motion_notifications', False), + '@motion_notifications_emails': ui.get('motion_notifications_emails', ''), + + # working schedule + '@working_schedule': '' + } + + if ui.get('text_overlay', False): + left_text = ui.get('left_text', 'camera-name') + if left_text == 'camera-name': + data['text_left'] = ui.get('name') + + elif left_text == 'timestamp': + data['text_left'] = '%Y-%m-%d\n%T' + + else: + data['text_left'] = ui.get('custom_left_text', '') + + right_text = ui.get('right_text', 'timestamp') + if right_text == 'camera-name': + data['text_right'] = ui.get('name') + + elif right_text == 'timestamp': + data['text_right'] = '%Y-%m-%d\n%T' + + else: + data['text_right'] = ui.get('custom_right_text', '') + + if ui.get('still_images', False): + capture_mode = ui.get('capture_mode', 'motion-triggered') + if capture_mode == 'motion-triggered': + data['output_normal'] = True + data['jpeg_filename'] = ui.get('image_file_name', '%Y-%m-%d-%H-%M-%S-%q') + + elif capture_mode == 'interval-snapshots': + data['snapshot_interval'] = int(ui.get('snapshot_interval'), 300) + data['snapshot_filename'] = ui.get('image_file_name', '%Y-%m-%d-%H-%M-%S-%q') + + elif capture_mode == 'all-frames': + data['output_all'] = True + data['jpeg_filename'] = ui.get('image_file_name', '%Y-%m-%d-%H-%M-%S') + + data['quality'] = max(1, int(ui.get('image_quality', 75))) + + if ui.get('motion_movies', False): + data['ffmpeg_variable_bitrate'] = 2 + int((100 - int(ui.get('movie_quality', 50))) * 0.29) + data['movie_filename'] = ui.get('movie_file_name', '%Y-%m-%d-%H-%M-%S-%q') + + if ui.get('working_schedule', False): + data['@working_schedule'] = ( + ui.get('monday_from', '') + '-' + ui.get('monday_to') + '|' + + ui.get('tuesday_from', '') + '-' + ui.get('tuesday_to') + '|' + + ui.get('wednesday_from', '') + '-' + ui.get('wednesday_to') + '|' + + ui.get('thursday_from', '') + '-' + ui.get('thursday_to') + '|' + + ui.get('friday_from', '') + '-' + ui.get('friday_to') + '|' + + ui.get('saturday_from', '') + '-' + ui.get('saturday_to') + '|' + + ui.get('sunday_from', '') + '-' + ui.get('sunday_to')) + + return data + + def _camera_dict_to_ui(self, data): + ui = { + # device + 'name': data['@name'], + 'enabled': data['@enabled'], + 'device': data['@proto'] + '://' + data['videodevice'], + 'light_switch_detect': data['lightswitch'] > 0, + 'auto_brightness': data['auto_brightness'], + 'brightness': int(int(data['brightness']) / 2.55), + 'contrast': int(int(data['contrast']) / 2.55), + 'saturation': int(int(data['saturation']) / 2.55), + 'hue': int(int(data['hue'])), + 'resolution': str(data['width']) + 'x' + str(data['height']), + 'framerate': int(data['framerate']), + + # file storage + 'storage_device': data['@storage_device'], + 'network_server': data['@network_server'], + 'network_share_name': data['@network_share_name'], + 'network_username': data['@network_username'], + 'network_password': data['@network_password'], + 'root_directory': data['target_dir'], + + # text overlay + 'text_overlay': False, + 'left_text': 'camera-name', + 'right_text': 'timestamp', + 'custom_left_text': '', + 'custom_right_text': '', + + # streaming + 'vudeo_streaming': not data['webcam_localhost'], + 'streaming_port': int(data['webcam_port']), + 'streaming_framerate': int(data['webcam_maxrate']), + 'streaming_quality': int(data['webcam_quality']), + 'streaming_motion': int(data['webcam_motion']), + + # still images + 'still_images': False, + 'capture_mode': 'motion-triggered', + 'image_file_name': '%Y-%m-%d-%H-%M-%S', + 'image_quality': 75, + 'snapshot_interval': 0, + 'preserve_images': data['@preserve_images'], + + # motion movies + 'motion_movies': False, + 'movie_quality': 50, + 'movie_file_name': '%Y-%m-%d-%H-%M-%S-%q', + 'preserve_movies': data['@preserve_movies'], + + # motion detection + 'show_frame_changes': data.get('text_changes') or data.get('locate'), + 'frame_change_threshold': data['threshold'], + 'auto_noise_detect': data['noise_tune'], + 'noise_level': int(int(data['noise_level']) / 2.55), + 'gap': int(data['gap']), + 'pre_capture': int(data['pre_capture']), + 'post_capture': int(data['post_capture']), + + # motion notifications + 'motion_notifications': data['@motion_notifications'], + 'motion_notifications_emails': data['@motion_notifications_emails'], + + # working schedule + 'working_schedule': False, + 'monday_from': '09:00', 'monday_to': '17:00', + 'tuesday_from': '09:00', 'tuesday_to': '17:00', + 'wednesday_from': '09:00', 'wednesday_to': '17:00', + 'thursday_from': '09:00', 'thursday_to': '17:00', + 'friday_from': '09:00', 'friday_to': '17:00', + 'saturday_from': '09:00', 'saturday_to': '17:00', + 'sunday_from': '09:00', 'sunday_to': '17:00' + } + + text_left = data['text_left'] + text_right = data['text_right'] + if text_left or text_right: + ui['text_overlay'] = True + + if text_left == data['@name']: + ui['left_text'] = 'camera-name' + + elif text_left == '%Y-%m-%d\n%T': + ui['left_text'] = 'timestamp' + + else: + ui['left_text'] = 'custom-text' + ui['custom_left_text'] = text_left + + if text_right == data['@name']: + ui['right_text'] = 'camera-name' + + elif text_right == '%Y-%m-%d\n%T': + ui['right_text'] = 'timestamp' + + else: + ui['right_text'] = 'custom-text' + ui['custom_right_text'] = text_right + + output_all = data.get('output_all') + output_normal = data.get('output_normal') + jpeg_filename = data.get('jpeg_filename') + snapshot_interval = data.get('snapshot_interval') + snapshot_filename = data.get('snapshot_filename') + + if (((output_all or output_normal) and jpeg_filename) or + (snapshot_interval and snapshot_filename)): + + ui['still_images'] = True + + if output_all: + ui['capture_mode'] = 'all-frames' + ui['image_file_name'] = jpeg_filename + + elif data.get('snapshot_interval'): + ui['capture-mode'] = 'interval-snapshots' + ui['image_file_name'] = snapshot_filename + ui['snapshot_interval'] = snapshot_interval + + elif data.get('output_normal'): + ui['capture-mode'] = 'motion-triggered' + ui['image_file_name'] = jpeg_filename + + ui['image_quality'] = ui.get('quality', 75) + + movie_filename = data.get('movie_filename') + if movie_filename: + ui['motion_movies'] = True + ui['movie_quality'] = int((max(2, data['ffmpeg_variable_bitrate']) - 2) / 0.29) + ui['movie_file_name'] = movie_filename + + working_schedule = data.get('@working_schedule') + if working_schedule: + days = working_schedule.split('|') + ui['monday_from'], ui['monday_to'] = days[0].split('-') + ui['tuesday_from'], ui['tuesday_to'] = days[1].split('-') + ui['wednesday_from'], ui['wednesday_to'] = days[2].split('-') + ui['thursday_from'], ui['thursday_to'] = days[3].split('-') + ui['friday_from'], ui['friday_to'] = days[4].split('-') + ui['saturday_from'], ui['saturday_to'] = days[5].split('-') + ui['sunday_from'], ui['sunday_to'] = days[6].split('-') + ui['working_schedule'] = True + + return ui + class SnapshotHandler(BaseHandler): def get(self, camera_id, op, filename=None): diff --git a/static/js/base-site.js b/static/js/base-site.js index 69be880..58e55d0 100644 --- a/static/js/base-site.js +++ b/static/js/base-site.js @@ -5,15 +5,18 @@ var noPushLock = 0; /* Ajax */ -function ajax(method, url, data, success) { +function ajax(method, url, data, callback) { var options = { type: method, url: url, data: data, cache: false, - success: success, + success: callback, failure: function (request, options, error) { alert('Request failed with code: ' + request.status); + if (callback) { + callback(); + } } }; @@ -29,10 +32,12 @@ function ajax(method, url, data, success) { /* UI */ function initUI() { + /* checkboxes */ $('input[type=checkbox].styled').each(function () { makeCheckBox($(this)); }); + /* sliders */ makeSlider($('#brightnessSlider'), 0, 100, 0, null, 5, 0, '%'); makeSlider($('#contrastSlider'), 0, 100, 0, null, 5, 0, '%'); makeSlider($('#saturationSlider'), 0, 100, 0, null, 5, 0, '%'); @@ -61,11 +66,14 @@ function initUI() { makeSlider($('#frameChangeThresholdSlider'), 0, 10000, 0, null, 3, 0, 'px'); makeSlider($('#noiseLevelSlider'), 0, 100, 0, null, 5, 0, '%'); - makeNumberValidator($('#snapshotIntervalEntry'), 1, 86400, false, false); - makeNumberValidator($('#gapEntry'), 1, 86400, false, false); - makeNumberValidator($('#preCaptureEntry'), 0, 100, false, false); - makeNumberValidator($('#postCaptureEntry'), 0, 100, false, false); + /* number validators */ + makeNumberValidator($('#streamingPortEntry'), 1024, 65535, false, false, true); + makeNumberValidator($('#snapshotIntervalEntry'), 1, 86400, false, false, true); + makeNumberValidator($('#gapEntry'), 1, 86400, false, false, true); + makeNumberValidator($('#preCaptureEntry'), 0, 100, false, false, true); + makeNumberValidator($('#postCaptureEntry'), 0, 100, false, false, true); + /* time validators */ makeTimeValidator($('#mondayFrom')); makeTimeValidator($('#mondayTo')); makeTimeValidator($('#tuesdayFrom')); @@ -81,6 +89,7 @@ function initUI() { makeTimeValidator($('#sundayFrom')); makeTimeValidator($('#sundayTo')); + /* ui elements that enable/disable other ui elements */ $('#motionEyeSwitch').change(updateConfigUI); $('#showAdvancedSwitch').change(updateConfigUI); $('#storageDeviceSelect').change(updateConfigUI); @@ -97,8 +106,18 @@ function initUI() { $('#motionNotificationsSwitch').change(updateConfigUI); $('#workingScheduleSwitch').change(updateConfigUI); - $('#videoDeviceSwitch').change(fetchCameraConfig); + /* fetch & push handlers */ + $('#videoDeviceSelect').change(fetchCameraConfig); $('input.general').change(pushMainConfig); + $('input.device, select.device, ' + + 'input.storage, select.storage, ' + + 'input.text-overlay, select.text-overlay, ' + + 'input.streaming, select.streaming, ' + + 'input.still-images, select.still-images, ' + + 'input.motion-movies, select.motion-movies, ' + + 'input.motion-detection, select.motion-detection, ' + + 'input.notifications, select.notifications, ' + + 'input.working-schedule, select.working-schedule').change(pushCameraConfig); } function updateConfigUI() { @@ -203,6 +222,23 @@ function updateConfigUI() { } }); + /* re-validate all the input validators */ + $('div.settings').find('input.number-validator, input.time-validator').each(function () { + this.validate(); + }); + + /* update all checkboxes and sliders */ + $('div.settings').find('input[type=checkbox], input.range').each(function () { + this.update(); + }); + + /* select the first option for the selects with no current selection */ + $('div.settings').find('select').each(function () { + if (this.selectedIndex === -1) { + this.selectedIndex = 0; + } + }); + noPushLock--; } @@ -221,22 +257,98 @@ function dict2MainUi(dict) { noPushLock++; $('#motionEyeSwitch')[0].checked = dict['@enabled']; - $('#motionEyeSwitch').change(); - $('#showAdvancedSwitch')[0].checked = dict['@show_advanced']; - $('#showAdvancedSwitch').change(); - $('#adminUsernameEntry').val(dict['@admin_username']); $('#adminPasswordEntry').val(dict['@admin_password']); $('#normalUsernameEntry').val(dict['@normal_username']); $('#normalPasswordEntry').val(dict['@normal_password']); + updateConfigUI(); + noPushLock--; } function cameraUi2Dict() { return { + /* video device */ + 'enabled': $('#videoDeviceSwitch')[0].checked, + 'name': $('#deviceNameEntry').val(), + 'device': $('#deviceEntry').val(), + 'light_switch_detect': $('#lightSwitchDetectSwitch')[0].checked, + 'auto_brightness': $('#autoBrightnessSwitch')[0].checked, + 'brightness': $('#brightnessSlider').val(), + 'contrast': $('#contrastSlider').val(), + 'saturation': $('#saturationSlider').val(), + 'hue': $('#hueSlider').val(), + 'resolution': $('#resolutionSelect').val(), + 'rotation': $('#rotationSelect').val(), + 'framerate': $('#framerateSlider').val(), + + /* file storage */ + 'storage_device': $('#storageDeviceSelect').val(), + 'network_server': $('#networkServerEntry').val(), + 'network_share_name': $('#networkShareNameEntry').val(), + 'network_username': $('#networkUsernameEntry').val(), + 'network_password': $('#networkPasswordEntry').val(), + 'root_directory': $('#rootDirectoryEntry').val(), + + /* text overlay */ + 'text_overlay': $('#textOverlaySwitch')[0].checked, + 'left_text': $('#leftTextSelect').val(), + 'custom_left_text': $('#leftTextEntry').val(), + 'right_text': $('#rightTextSelect').val(), + 'custom_right_text': $('#rightTextEntry').val(), + + /* video streaming */ + 'video_streaming': $('#videoStreamingSwitch')[0].checked, + 'streaming_port': $('#streamingPortEntry').val(), + 'streaming_framerate': $('#streamingFramerateSlider').val(), + 'streaming_quality': $('#streamingQualitySlider').val(), + 'streaming_motion': $('#streamingMotion')[0].checked, + + /* still images */ + 'still_images': $('#stillImagesSwitch')[0].checked, + 'image_file_name': $('#imageFileNameEntry').val(), + 'image_quality': $('#imageQualitySlider').val(), + 'capture_mode': $('#captureModeSelect').val(), + 'snapshot_interval': $('#snapshotIntervalEntry').val(), + 'preserve_images': $('#preserveImagesSelect').val(), + + /* motion movies */ + 'motion_movies': $('#motionMoviesSwitch')[0].checked, + 'movie_file_name': $('#movieFileNameEntry').val(), + 'movie_quality': $('#movieQualitySlider').val(), + 'preserve_movies': $('#preserveMoviesSelect').val(), + + /* motion detection */ + 'show_frame_changes': $('#showFrameChangesSwitch')[0].checked, + 'frame_change_threshold': $('#frameChangeThresholdSlider').val(), + 'auto_noise_detect': $('#autoNoiseDetectSwitch')[0].checked, + 'noise_level': $('#noiseLevelSlider').val(), + 'gap': $('#gapEntry').val(), + 'pre_capture': $('#preCaptureEntry').val(), + 'post_capture': $('#postCaptureEntry').val(), + /* motion notifications */ + 'motion_notifications': $('#motionNotificationsSwitch')[0].checked, + 'motion_notifications_emails': $('#emailAddressesEntry').val(), + + /* working schedule */ + 'working_schedule': $('#workingScheduleSwitch')[0].checked, + 'monday_from': $('#mondayFrom').val(), + 'monday_to':$('#mondayTo').val(), + 'tuesday_from': $('#tuesdayFrom').val(), + 'tuesday_to': $('#tuesdayTo').val(), + 'wednesday_from': $('#wednesdayFrom').val(), + 'wednesday_to': $('#wednesdayTo').val(), + 'thursday_from': $('#thursdayFrom').val(), + 'thursday_to': $('#thursdayTo').val(), + 'friday_from':$('#fridayFrom').val(), + 'friday_to': $('#fridayTo').val(), + 'saturday_from':$('#saturdayFrom').val(), + 'saturday_to': $('#saturdayTo').val(), + 'sunday_from': $('#sundayFrom').val(), + 'sunday_to': $('#sundayTo').val(), }; } @@ -244,83 +356,86 @@ function dict2CameraUi(dict) { noPushLock++; /* video device */ - $('#videoDeviceSwitch'); - $('#deviceNameEntry'); - $('#lightSwitchDetectSwitch'); - $('#autoBrightnessSwitch'); - $('#brightnessSlider'); - $('#constrastSlider'); - $('#saturationSlider'); - $('#hueSlider'); - $('#resolutionSelect'); - $('#rotationSelect'); - $('#framerateSlider'); + $('#videoDeviceSwitch')[0].checked = dict['enabled']; + $('#deviceNameEntry').val(dict['name']); + $('#deviceEntry').val(dict['device']); + $('#lightSwitchDetectSwitch')[0].checked = dict['light_switch_detect']; + $('#autoBrightnessSwitch')[0].checked = dict['auto_brightness']; + $('#brightnessSlider').val(dict['brightness']); + $('#contrastSlider').val(dict['contrast']); + $('#saturationSlider').val(dict['saturation']); + $('#hueSlider').val(dict['hue']); + $('#resolutionSelect').val(dict['resolution']); + $('#rotationSelect').val(dict['rotation']); + $('#framerateSlider').val(dict['framerate']); /* file storage */ - $('#storageDeviceSelect'); - $('#networkServerEntry'); - $('#networkShareNameEntry'); - $('#networkUsernameEntry'); - $('#networkPasswordEntry'); - $('#rootDirectoryEntry'); + $('#storageDeviceSelect').val(dict['storage_device']); + $('#networkServerEntry').val(dict['network_server']); + $('#networkShareNameEntry').val(dict['network_share_name']); + $('#networkUsernameEntry').val(dict['network_username']); + $('#networkPasswordEntry').val(dict['network_password']); + $('#rootDirectoryEntry').val(dict['root_directory']); /* text overlay */ - $('#textOverlaySwitch'); - $('#leftTextSelect'); - $('#leftTextEntry'); - $('#rightTextSelect'); - $('#rightTextEntry'); + $('#textOverlaySwitch')[0].checked = dict['text_overlay']; + $('#leftTextSelect').val(dict['left_text']); + $('#leftTextEntry').val(dict['custom_left_text']); + $('#rightTextSelect').val(dict['right_text']); + $('#rightTextEntry').val(dict['custom_right_text']); /* video streaming */ - $('#videoStreamingSwitch'); - $('#streamingFramerateSlider'); - $('#streamingQualitySlider'); - $('#motionOptimizationSwitch'); + $('#videoStreamingSwitch')[0].checked = dict['video_streaming']; + $('#streamingPortEntry').val(dict['streaming_port']); + $('#streamingFramerateSlider').val(dict['streaming_framerate']); + $('#streamingQualitySlider').val(dict['streaming_quality']); + $('#streamingMotion')[0].checked = dict['streaming_motion']; /* still images */ - $('#stillImagesSwitch'); - $('#imageFileNameEntry'); - $('#imageQualitySlider'); - $('#captureModeSelect'); - $('#snapshotIntervalEntry'); - $('#preserveImagesSelect'); + $('#stillImagesSwitch')[0].checked = dict['still_images']; + $('#imageFileNameEntry').val(dict['image_file_name']); + $('#imageQualitySlider').val(dict['image_quality']); + $('#captureModeSelect').val(dict['capture_mode']); + $('#snapshotIntervalEntry').val(dict['snapshot_interval']); + $('#preserveImagesSelect').val(dict['preserve_images']); /* motion movies */ - $('#motionMoviesSwitch'); - $('#movieFileNameEntry'); - $('#movieQualitySlider'); - $('#preserveMoviesSelect'); + $('#motionMoviesSwitch')[0].checked = dict['motion_movies']; + $('#movieFileNameEntry').val(dict['movie_file_name']); + $('#movieQualitySlider').val(dict['movie_quality']); + $('#preserveMoviesSelect').val(dict['preserve_movies']); /* motion detection */ - $('#showFrameChangesSwitch'); - $('#frameChangeThresholdSlider'); - $('#autoNoiseDetectSwitch'); - $('#noiseLevelSlider'); - $('#gapEntry'); - $('#preCaptureEntry'); - $('#postCaptureEntry'); + $('#showFrameChangesSwitch')[0].checked = dict['show_frame_changes']; + $('#frameChangeThresholdSlider').val(dict['frame_change_threshold']); + $('#autoNoiseDetectSwitch')[0].checked = dict['auto_noise_detect']; + $('#noiseLevelSlider').val(dict['noise_level']); + $('#gapEntry').val(dict['gap']); + $('#preCaptureEntry').val(dict['pre_capture']); + $('#postCaptureEntry').val(dict['post_capture']); /* motion notifications */ - $('#motionNotificationsSwitch'); - $('#emailAddressEntry'); - $('#phoneNumberEntry'); + $('#motionNotificationsSwitch')[0].checked = dict['motion_notifications']; + $('#emailAddressesEntry').val(dict['motion_notifications_emails']); /* working schedule */ - $('#workingScheduleSwitch'); - $('#mondayFrom'); - $('#mondayTo'); - $('#tuesdayFrom'); - $('#tuesdayTo'); - $('#wednesdayFrom'); - $('#wednesdayTo'); - $('#thursdayFrom'); - $('#thursdayTo'); - $('#fridayFrom'); - $('#fridayTo'); - $('#saturdayFrom'); - $('#saturdayTo'); - $('#sundayFrom'); - $('#sundayTo'); + $('#workingScheduleSwitch')[0].checked = dict['working_schedule']; + $('#mondayFrom').val(dict['monday_from']); + $('#mondayTo').val(dict['monday_to']); + $('#tuesdayFrom').val(dict['tuesday_from']); + $('#tuesdayTo').val(dict['tuesday_to']); + $('#wednesdayFrom').val(dict['wednesday_from']); + $('#wednesdayTo').val(dict['wednesday_to']); + $('#thursdayFrom').val(dict['thursday_from']); + $('#thursdayTo').val(dict['thursday_to']); + $('#fridayFrom').val(dict['friday_from']); + $('#fridayTo').val(dict['friday_to']); + $('#saturdayFrom').val(dict['saturday_from']); + $('#saturdayTo').val(dict['saturday_to']); + $('#sundayFrom').val(dict['sunday_from']); + $('#sundayTo').val(dict['sunday_to']); + + updateConfigUI(); noPushLock--; } @@ -365,10 +480,27 @@ function pushMainConfig() { return; } + noPushLock++; + var mainConfig = mainUi2Dict(); ajax('POST', '/config/main/set/', mainConfig, function () { - + noPushLock--; + }); +} + +function pushCameraConfig() { + if (noPushLock) { + return; + } + + noPushLock++; + + var cameraConfig = cameraUi2Dict(); + var cameraId = $('#videoDeviceSelect').val(); + + ajax('POST', '/config/' + cameraId + '/set/', cameraConfig, function () { + noPushLock--; }); } diff --git a/static/js/ui.js b/static/js/ui.js index 8fbb1a3..f9b8747 100644 --- a/static/js/ui.js +++ b/static/js/ui.js @@ -23,15 +23,17 @@ function makeCheckBox($input) { /* add the element */ $input.after(mainDiv); - /* add event handers */ - $input.change(function () { - if (this.checked) { + function update() { + if ($input[0].checked) { setOn(); } else { setOff(); } - }).change(); + } + + /* add event handers */ + $input.change(update).change(); mainDiv.click(function () { $input[0].checked = !$input[0].checked; @@ -51,6 +53,8 @@ function makeCheckBox($input) { } }); + $input[0].update = update; + return mainDiv; } @@ -134,6 +138,8 @@ function makeSlider($input, minVal, maxVal, snapMode, ticks, ticksNumber, decima $('body').unbind('mousemove', bodyMouseMove); $('body').unbind('mouseup', bodyMouseUp); + + $input.change(); } bar.mousedown(function (e) { @@ -212,6 +218,8 @@ function makeSlider($input, minVal, maxVal, snapMode, ticks, ticksNumber, decima } }); + $input[0].update = input2slider; + return slider; } @@ -258,7 +266,7 @@ function makeNumberValidator($input, minVal, maxVal, floating, sign, required) { msg = 'enter a positive'; } else { - msg = 'enter a' + msg = 'enter a'; } if (floating) { msg += ' number'; @@ -294,6 +302,9 @@ function makeNumberValidator($input, minVal, maxVal, floating, sign, required) { $input.keyup(validate); $input.change(validate).change(); + + $input.addClass('number-validator'); + $input[0].validate = validate; } function makeTimeValidator($input) { @@ -322,4 +333,7 @@ function makeTimeValidator($input) { selectOnBlur: true, timeFormat: 'H:i', }); + + $input.addClass('time-validator'); + $input[0].validate = validate; } diff --git a/templates/base-site.html b/templates/base-site.html index 15b2c50..138c010 100644 --- a/templates/base-site.html +++ b/templates/base-site.html @@ -67,6 +67,10 @@ ? + + Camera Device + +
@@ -217,10 +221,15 @@ ? + + Streaming Port + + ? + Motion Optimization - - ? + + ? @@ -256,11 +265,11 @@ Preserve Images ? @@ -283,11 +292,11 @@ Preserve Movies ? @@ -339,15 +348,10 @@
Motion Notifications
- - + + - - - - -
Email AddressEmail Addresses ?
Phone Number?
Working Schedule