From 55035be4624bad97e8fd262aa5b91aa196c373e2 Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Sat, 5 Aug 2017 22:26:43 +0300 Subject: [PATCH] implement manual snapshot button --- motioneye/config.py | 22 +++++++++++++++++----- motioneye/handlers.py | 15 ++++++++------- motioneye/motionctl.py | 33 +++++++++++++++++++++++++++++++-- motioneye/static/js/main.js | 2 ++ motioneye/templates/main.html | 5 +++++ 5 files changed, 63 insertions(+), 14 deletions(-) diff --git a/motioneye/config.py b/motioneye/config.py index efd90ed..e7614e5 100644 --- a/motioneye/config.py +++ b/motioneye/config.py @@ -40,8 +40,8 @@ import v4l2ctl _CAMERA_CONFIG_FILE_NAME = 'thread-%(id)s.conf' _MAIN_CONFIG_FILE_NAME = 'motion.conf' -_ACTIONS = ['lock', 'unlock', 'light_on', 'light_off', 'alarm_on', 'alarm_off', 'up', - 'right', 'down', 'left', 'zoom_in', 'zoom_out', +_ACTIONS = ['lock', 'unlock', 'light_on', 'light_off', 'alarm_on', 'alarm_off', + 'up', 'right', 'down', 'left', 'zoom_in', 'zoom_out', 'preset1', 'preset2', 'preset3', 'preset4', 'preset5', 'preset6', 'preset7', 'preset8', 'preset9'] _main_config_cache = None @@ -774,6 +774,7 @@ def motion_camera_ui_to_dict(ui, old_config=None): 'snapshot_filename': ui['image_file_name'], 'quality': max(1, int(ui['image_quality'])), '@preserve_pictures': int(ui['preserve_pictures']), + '@manual_snapshots': ui['manual_snapshots'], # movies 'ffmpeg_output_movies': False, @@ -1161,6 +1162,7 @@ def motion_camera_dict_to_ui(data): 'image_quality': data['quality'], 'snapshot_interval': 0, 'preserve_pictures': data['@preserve_pictures'], + 'manual_snapshots': data['@manual_snapshots'], # movies 'movies': False, @@ -1527,7 +1529,7 @@ def motion_camera_dict_to_ui(data): ui['extra_options'] = extra_options # action commands - action_commands = get_action_commands(data['@id']) + action_commands = get_action_commands(data) ui['actions'] = action_commands.keys() return ui @@ -1571,19 +1573,27 @@ def simple_mjpeg_camera_dict_to_ui(data): ui[name[1:]] = value # action commands - action_commands = get_action_commands(data['@id']) + action_commands = get_action_commands(data) ui['actions'] = action_commands.keys() return ui -def get_action_commands(camera_id): +def get_action_commands(camera_config): + camera_id = camera_config['@id'] + action_commands = {} for action in _ACTIONS: path = os.path.join(settings.CONF_PATH, '%s_%s' % (action, camera_id)) if os.access(path, os.X_OK): action_commands[action] = path + if camera_config.get('@manual_snapshots'): + action_commands['snapshot'] = True + + if camera_config.get('@manual_record'): + action_commands['record'] = True + return action_commands @@ -1964,6 +1974,7 @@ def _set_default_motion_camera(camera_id, data): data.setdefault('snapshot_filename', '') data.setdefault('quality', 85) data.setdefault('@preserve_pictures', 0) + data.setdefault('@manual_snapshots', True) data.setdefault('movie_filename', '%Y-%m-%d/%H-%M-%S') data.setdefault('max_movie_time', 0) @@ -1981,6 +1992,7 @@ def _set_default_motion_camera(camera_id, data): data.setdefault('ffmpeg_variable_bitrate', _EXPONENTIAL_DEF_QUALITY) data.setdefault('@preserve_movies', 0) + data.setdefault('@manual_record', False) data.setdefault('@working_schedule', '') data.setdefault('@working_schedule_type', 'outside') diff --git a/motioneye/handlers.py b/motioneye/handlers.py index 0baab08..e483e2b 100644 --- a/motioneye/handlers.py +++ b/motioneye/handlers.py @@ -1646,17 +1646,17 @@ class ActionHandler(BaseHandler): if action == 'snapshot': logging.debug('executing snapshot action for camera with id %s' % camera_id) - return self.snapshot() + return self.snapshot(camera_id) elif action == 'record_start': logging.debug('executing record_start action for camera with id %s' % camera_id) - return self.record_start() + return self.record_start(camera_id) elif action == 'record_stop': logging.debug('executing record_stop action for camera with id %s' % camera_id) - return self.record_stop() + return self.record_stop(camera_id) - action_commands = config.get_action_commands(camera_id) + action_commands = config.get_action_commands(local_config) command = action_commands.get(action) if not command: raise HTTPError(400, 'unknown action') @@ -1694,13 +1694,14 @@ class ActionHandler(BaseHandler): else: self.io_loop.add_timeout(datetime.timedelta(milliseconds=100), self.check_command) - def snapshot(self): + def snapshot(self, camera_id): + motionctl.take_snapshot(camera_id) self.finish_json({}) - def record_start(self): + def record_start(self, camera_id): self.finish_json({}) - def record_stop(self): + def record_stop(self, camera_id): self.finish_json({}) diff --git a/motioneye/motionctl.py b/motioneye/motionctl.py index cc4611b..a34e0e5 100644 --- a/motioneye/motionctl.py +++ b/motioneye/motionctl.py @@ -223,7 +223,8 @@ def get_motion_detection(camera_id, callback): logging.error(error) return callback(error=error) - url = 'http://127.0.0.1:7999/%(id)s/detection/status' % {'id': thread_id} + url = 'http://127.0.0.1:%(port)s/%(id)s/detection/status' % { + 'port': settings.MOTION_CONTROL_PORT, 'id': thread_id} def on_response(response): if response.error: @@ -256,7 +257,8 @@ def set_motion_detection(camera_id, enabled): 'what': ['disabling', 'enabling'][enabled], 'id': camera_id}) - url = 'http://127.0.0.1:7999/%(id)s/detection/%(enabled)s' % { + url = 'http://127.0.0.1:%(port)s/%(id)s/detection/%(enabled)s' % { + 'port': settings.MOTION_CONTROL_PORT, 'id': thread_id, 'enabled': ['pause', 'start'][enabled]} @@ -277,6 +279,33 @@ def set_motion_detection(camera_id, enabled): http_client.fetch(request, on_response) +def take_snapshot(camera_id): + 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) + + logging.debug('taking snapshot for camera with id %(id)s' % {'id': camera_id}) + + url = 'http://127.0.0.1:%(port)s/%(id)s/action/snapshot' % { + 'port': settings.MOTION_CONTROL_PORT, + 'id': thread_id} + + def on_response(response): + if response.error: + logging.error('failed to take snapshot for camera with id %(id)s: %(msg)s' % { + 'id': camera_id, + 'msg': utils.pretty_http_error(response)}) + + else: + logging.debug('successfully took snapshot for camera with id %(id)s' % {'id': camera_id}) + + request = HTTPRequest(url, connect_timeout=_MOTION_CONTROL_TIMEOUT, request_timeout=_MOTION_CONTROL_TIMEOUT) + http_client = AsyncHTTPClient() + http_client.fetch(request, on_response) + + def is_motion_detected(camera_id): return _motion_detected.get(camera_id, False) diff --git a/motioneye/static/js/main.js b/motioneye/static/js/main.js index 56b0139..1e1d645 100644 --- a/motioneye/static/js/main.js +++ b/motioneye/static/js/main.js @@ -1878,6 +1878,7 @@ function cameraUi2Dict() { 'capture_mode': $('#captureModeSelect').val(), 'snapshot_interval': $('#snapshotIntervalEntry').val(), 'preserve_pictures': $('#preservePicturesSelect').val() >= 0 ? $('#preservePicturesSelect').val() : $('#picturesLifetimeEntry').val(), + 'manual_snapshots': $('#manualSnapshotsSwitch')[0].checked, /* movies */ 'movies': $('#moviesEnabledSwitch')[0].checked, @@ -2230,6 +2231,7 @@ function dict2CameraUi(dict) { } markHideIfNull('preserve_pictures', 'preservePicturesSelect'); $('#picturesLifetimeEntry').val(dict['preserve_pictures']); markHideIfNull('preserve_pictures', 'picturesLifetimeEntry'); + $('#manualSnapshotsSwitch')[0].checked = dict['manual_snapshots']; markHideIfNull('manual_snapshots', 'manualSnapshotsSwitch'); /* movies */ $('#moviesEnabledSwitch')[0].checked = dict['movies']; markHideIfNull('movies', 'moviesEnabledSwitch'); diff --git a/motioneye/templates/main.html b/motioneye/templates/main.html index 27cc16a..cf80291 100644 --- a/motioneye/templates/main.html +++ b/motioneye/templates/main.html @@ -721,6 +721,11 @@ days ? + + Manual Snapshots + + ? + {% for config in camera_sections.get('still-images', {}).get('configs', []) %} {{config_item(config)}} {% endfor %} -- 2.39.5