# along with this program. If not, see <http://www.gnu.org/licenses/>.
import base64
+import functools
import json
import logging
import os
self.finish_json(ui_config)
@BaseHandler.auth(admin=True)
- def set_config(self, camera_id, ui_config=None, no_finish=False):
- if ui_config is None:
- try:
- ui_config = json.loads(self.request.body)
-
- except Exception as e:
- logging.error('could not decode json: %(msg)s' % {'msg': unicode(e)})
-
- raise
+ def set_config(self, camera_id):
+ try:
+ ui_config = json.loads(self.request.body)
+
+ except Exception as e:
+ logging.error('could not decode json: %(msg)s' % {'msg': unicode(e)})
- reload = False
+ raise
- if camera_id is not None:
- if camera_id == 0:
- logging.debug('setting multiple configs')
-
- for key, cfg in ui_config.items():
- if key == 'main':
- reload = self.set_config(None, cfg, no_finish=True) or reload
-
- else:
- reload = self.set_config(int(key), cfg, no_finish=True) or reload
-
- return self.finish_json({'reload': reload})
-
- logging.debug('setting config for camera %(id)s' % {'id': camera_id})
+ camera_ids = config.get_camera_ids()
+
+ def set_camera_config(camera_id, ui_config, on_finish):
+ logging.debug('setting config for camera %(id)s...' % {'id': camera_id})
- camera_ids = config.get_camera_ids()
if camera_id not in camera_ids:
raise HTTPError(404, 'no such camera')
local_config = config.get_camera(camera_id)
if local_config['@proto'] == 'v4l2':
-# ui_config.setdefault('device', local_config.get('videodevice', '')) TODO needed?
-# ui_config.setdefault('proto', local_config['@proto'])
-# ui_config.setdefault('enabled', local_config['@enabled'])
+ # overwrite some fields whose values should not be changed this way
+ ui_config['device_uri'] = local_config['videodevice']
+ ui_config['proto'] = 'v4l2'
+ ui_config['host'] = ''
+ ui_config['port'] = ''
+ ui_config.setdefault('enabled', True)
local_config = config.camera_ui_to_dict(ui_config)
config.set_camera(camera_id, local_config)
-
- else: # remote camera
+
+ on_finish(None, True) # (no error, motion needs restart)
+
+ else:
# update the camera locally
local_config['@enabled'] = ui_config['enabled']
config.set_camera(camera_id, local_config)
# the camera was probably disabled due to errors
if ui_config.has_key('device_uri'):
- # remove the fields that should not get to the remote side
+ # delete some fields that should not get to the remote side as they are
del ui_config['device_uri']
del ui_config['proto']
del ui_config['host']
del ui_config['port']
del ui_config['enabled']
- try:
- remote.set_config(local_config, ui_config)
-
- except Exception as e:
- logging.error('failed to set remote camera config: %(msg)s' % {'msg': unicode(e)})
-
- if not no_finish:
- return self.finish_json({'error': unicode(e)})
-
- elif not no_finish:
- return self.finish_json({'error': unicode(e)})
+ def on_finish_wrapper(error):
+ return on_finish(error, False)
+
+ remote.set_config(local_config, ui_config, on_finish_wrapper)
- else:
- logging.debug('setting main config')
+ def set_main_config(ui_config):
+ logging.debug('setting main config...')
old_main_config = config.get_main()
old_admin_credentials = old_main_config.get('@admin_username', '') + ':' + old_main_config.get('@admin_password', '')
if admin_credentials != old_admin_credentials:
logging.debug('admin credentials changed, reload needed')
- reload = True
+ return True # needs browser reload
+
+ return False
+
+ reload = False # indicates that browser should reload the page
+ restart = [False] # indicates that the local motion instance was modified and needs to be restarted
+ error = [None]
+
+ def finish():
+ if restart[0]:
+ logging.debug('motion needs to be restarted')
+ motionctl.restart()
- motionctl.restart() # TODO should not be restarted unless a local camera was changed
+ self.finish({'reload': reload, 'error': error[0]})
- if not no_finish:
- self.finish_json()
+ if camera_id is not None:
+ if camera_id == 0: # multiple camera configs
+ logging.debug('setting multiple configs')
+
+ so_far = [0]
+ def check_finished(e, r):
+ restart[0] = restart[0] or r
+ error[0] = error[0] or e
+ so_far[0] += 1
+
+ if so_far[0] >= len(ui_config): # finished
+ finish()
- return reload
+ for key, cfg in ui_config.items():
+ if key == 'main':
+ reload = set_main_config(cfg) or reload
+
+ else:
+ set_camera_config(int(key), cfg, check_finished)
+
+ else: # single camera config
+ def on_finish(e, r):
+ error[0] = e
+ restart[0] = r
+ finish()
+
+ set_camera_config(camera_id, ui_config, on_finish)
+
+ else: # main config
+ reload = set_main_config(ui_config)
@BaseHandler.auth(admin=True)
def set_preview(self, camera_id):
return True
- if self.error is None:
+ error = getattr(self, 'error', None)
+ if error is None:
return False
- self._error(self.error)
+ self._error(error)
return True
import json
import logging
-from tornado.httpclient import AsyncHTTPClient, HTTPClient, HTTPRequest
+from tornado.httpclient import AsyncHTTPClient, HTTPRequest
import settings
http_client.fetch(request, on_response)
-def set_config(local_config, camera_config):
+def set_config(local_config, ui_config, callback):
host = local_config.get('@host', local_config.get('host'))
port = local_config.get('@port', local_config.get('port'))
username = local_config.get('@username', local_config.get('username'))
'host': host,
'port': port})
- camera_config = json.dumps(camera_config)
+ ui_config = json.dumps(ui_config)
- request = _make_request(host, port, username, password, '/config/%(id)s/set/' % {'id': camera_id}, method='POST', data=camera_config)
+ request = _make_request(host, port, username, password, '/config/%(id)s/set/' % {'id': camera_id}, method='POST', data=ui_config)
- try:
- http_client = HTTPClient()
- response = http_client.fetch(request)
+ def on_response(response):
if response.error:
- raise Exception(unicode(response.error))
-
- except Exception as e:
- logging.error('failed to set config for remote camera %(id)s on %(host)s:%(port)s: %(msg)s' % {
- 'id': camera_id,
- 'host': host,
- 'port': port,
- 'msg': unicode(e)})
-
- raise
+ logging.error('failed to set config for remote camera %(id)s on %(host)s:%(port)s: %(msg)s' % {
+ 'id': camera_id,
+ 'host': host,
+ 'port': port,
+ 'msg': unicode(response.error)})
+
+ return callback(response.error)
+
+ callback(None)
+
+ http_client = AsyncHTTPClient()
+ http_client.fetch(request, on_response)
def set_preview(local_config, controls, callback):
/* show the camera progress indicators */
if (cameraIds) {
cameraIds.forEach(function (cameraId) {
- $('div.camera-frame#' + cameraId + ' div.camera-progress').css('opacity', '0.5');
+ $('div.camera-frame#camera' + cameraId + ' div.camera-progress').css('opacity', '0.5');
});
}
else {
return;
}
- beginProgress();
+ /* gather the affected motion instances */
+ var affectedInstances = {};
+ Object.keys(pushConfigs).forEach(function (key) {
+ var config = pushConfigs[key];
+ if (key === 'main') {
+ return;
+ }
+
+ var instance = config.host || '';
+ if (config.port) {
+ instance += ':' + config.port;
+ }
+
+ affectedInstances[instance] = true;
+ });
+ affectedInstances = Object.keys(affectedInstances);
+
+ /* compute the affected camera ids */
+ var cameraIdsByInstance = getCameraIdsByInstance();
+ var affectedCameraIds = [];
+
+ affectedInstances.forEach(function (instance) {
+ affectedCameraIds = affectedCameraIds.concat(cameraIdsByInstance[instance] || []);
+ });
+
+ beginProgress(affectedCameraIds);
ajax('POST', '/config/0/set/', pushConfigs, function (data) {
if (data == null || data.error) {
}
function getCameraIdsByInstance() {
+ /* a motion instance is identified by the (host, port) pair;
+ * the local instance has both the host and the port set to empty string */
+
+ var cameraIdsByInstance = {};
+ $('div.camera-frame').each(function () {
+ var instance = this.config.host || '';
+ if (this.config.port) {
+ instance += ':' + this.config.port;
+ }
+
+ (cameraIdsByInstance[instance] = cameraIdsByInstance[instance] || []).push(this.config.id);
+ });
+ return cameraIdsByInstance;
}
/* camera frames */
-function addCameraFrameUi(cameraId, cameraName, framerate) {
+function addCameraFrameUi(cameraConfig, framerate) {
var pageContainer = $('div.page-container');
- if (cameraId == null) {
+ if (cameraConfig == null) {
var cameraFrameDivPlaceHolder = $('<div class="camera-frame-place-holder"></div>');
pageContainer.append(cameraFrameDivPlaceHolder);
return;
}
+ var cameraId = cameraConfig.id;
+
var cameraFrameDiv = $(
'<div class="camera-frame">' +
'<div class="camera-top-bar">' +
'<div class="button camera-button mouse-effect media-pictures" title="pictures"></div>' +
'<div class="button camera-button mouse-effect media-movies" title="movies"></div>' +
'<div class="button camera-button mouse-effect configure" title="configure"></div>' +
-// '<div class="button camera-button mouse-effect full-screen" title="full screen"></div>' +
'</div>' +
'</div>' +
'<div class="camera-container">' +
cameraFrameDiv.attr('id', 'camera' + cameraId);
cameraFrameDiv[0].framerate = framerate;
cameraFrameDiv[0].refreshDivider = 0;
- nameSpan.html(cameraName);
+ cameraFrameDiv[0].config = cameraConfig;
+ nameSpan.html(cameraConfig.name);
progressImg.attr('src', staticUrl + 'img/camera-progress.gif');
cameraProgress.click(function () {
/* add camera frames */
for (i = 0; i < cameras.length; i++) {
camera = cameras[i];
- addCameraFrameUi(camera.id, camera.name, Math.min(camera.streaming_framerate, camera.framerate));
+ addCameraFrameUi(camera, Math.min(camera.streaming_framerate, camera.framerate));
}
if ($('#videoDeviceSelect').find('option').length < 2 && user === 'admin' && $('#motionEyeSwitch')[0].checked) {