implement manual snapshot button
authorCalin Crisan <ccrisan@gmail.com>
Sat, 5 Aug 2017 19:26:43 +0000 (22:26 +0300)
committerCalin Crisan <ccrisan@gmail.com>
Sat, 5 Aug 2017 19:26:43 +0000 (22:26 +0300)
motioneye/config.py
motioneye/handlers.py
motioneye/motionctl.py
motioneye/static/js/main.js
motioneye/templates/main.html

index efd90ed2629eedfd87e134a78b733bd23384ec5d..e7614e5a15cafb0de740984010d07c879ae9d32c 100644 (file)
@@ -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')
index 0baab08288e1148e2447671161777808f56200aa..e483e2b91649a1b0ce664f84680322619a4a3668 100644 (file)
@@ -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({})
 
 
index cc4611bbb1fcb81a6abca678d10d8e02aab4d17f..a34e0e5d0bc10f9e1d3c9a70f99acae36e677efa 100644 (file)
@@ -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)
 
index 56b01397df01374da64f4cee862b60262559b099..1e1d6453d6e33d66257181d76ba64645e74d78ec 100644 (file)
@@ -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');
index 27cc16a5f1e8396c1f2c82b0e4f49e64e3503bda..cf802911fec8dbb6390168049838d68a908755b3 100644 (file)
                         <td class="settings-item-value"><input type="text" class="styled number still-images camera-config" id="picturesLifetimeEntry"><span class="settings-item-unit">days</span></td>
                         <td><span class="help-mark" title="sets the number of days after which the pictures will be deleted automatically">?</span></td>
                     </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Manual Snapshots</span></td>
+                        <td class="settings-item-value"><input type="checkbox" class="styled still-images camera-config" id="manualSnapshotsSwitch"></td>
+                        <td><span class="help-mark" title="when this is enabled, a button on the camera frame will allow you to manually take snapshots">?</span></td>
+                    </tr>
                     {% for config in camera_sections.get('still-images', {}).get('configs', []) %}
                         {{config_item(config)}}
                     {% endfor %}