-> camera not available background and icon design
-> remove current snapshot GET logs
-> a nice shadow behind the header
+-> add a motion running status indicator (and maybe a start/stop button)
-> group @config rules to top
-> browser compatibility test
#!/usr/bin/env python2
import logging
+import motionctl
import os.path
import signal
import sys
ioloop.stop()
logging.info('server stopped')
+
+ if motionctl.running():
+ motionctl.stop()
+ logging.info('motion stopped')
signal.signal(signal.SIGINT, bye_handler)
signal.signal(signal.SIGTERM, bye_handler)
tornado.ioloop.IOLoop.instance().start()
+def _start_motion():
+ if not motionctl.running():
+ motionctl.start()
+ logging.info('motion started')
+
+
if __name__ == '__main__':
_configure_signals()
_configure_logging()
+ _start_motion()
_start_server()
-
-# import config # TODO remove this
-# main_config = config.get_main()
-# config.add_camera('v4l2:///dev/video0')
-# #data = config.get_camera(1)
-# #data['@enabled'] = True
-# #config.set_camera(1, data)
-# config.rem_camera(1)
-
-# import motionctl
-# motionctl.start()
data.setdefault('quality', 75)
data.setdefault('@preserve_images', 0)
- data.setdefault('movie_filename', '')
data.setdefault('ffmpeg_variable_bitrate', 14)
+ data.setdefault('movie_filename', '')
+ data.setdefault('ffmpeg_cap_new', False)
data.setdefault('@preserve_movies', 0)
data.setdefault('@motion_notifications', False)
import config
import mjpgclient
+import motionctl
import template
import v4l2ctl
raise
- if camera_id:
- 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')
-
- data = self._camera_ui_to_dict(data)
- config.set_camera(camera_id, data)
-
- else:
- logging.debug('setting main config')
-
- try:
- data = json.loads(self.request.body)
-
- except Exception as e:
- logging.error('could not decode json: %(msg)s' % {'msg': unicode(e)})
+ restart = bool(data.get('restart'))
+ if restart and motionctl.running():
+ motionctl.stop()
+
+ try:
+ if camera_id:
+ logging.debug('setting config for camera %(id)s' % {'id': camera_id})
- raise
-
- data = self._main_ui_to_dict(data)
- config.set_main(data)
+ 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:
+ logging.debug('setting main config')
+
+ try:
+ data = json.loads(self.request.body)
+
+ except Exception as e:
+ logging.error('could not decode json: %(msg)s' % {'msg': unicode(e)})
+
+ raise
+
+ data = self._main_ui_to_dict(data)
+ config.set_main(data)
+
+ except:
+ raise
+
+ finally:
+ if restart:
+ motionctl.start()
+
def set_preview(self, camera_id):
try:
controls = json.loads(self.request.body)
'@preserve_images': int(ui.get('preserve_images', 0)),
# movies
- 'ffmpeg_variable_bitrate': 0,
- 'ffmpeg_video_codec': 'mpeg4',
- 'ffmpeg_cap_new': True,
- 'movie_filename': '',
+ 'ffmpeg_variable_bitrate': 2 + int((100 - int(ui.get('movie_quality', 75))) * 0.29),
+ 'ffmpeg_cap_new': ui.get('motion_movies', False),
+ 'movie_filename': ui.get('movie_file_name', '%Y-%m-%d-%H-%M-%S-%q'),
'@preserve_movies': int(ui.get('preserve_movies', 0)),
# motion detection
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', 75))) * 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') + '|' +
background-color: #555;
}
+div.camera-progress {
+ position: absolute;
+ top: 0px;
+ right: 0px;
+ bottom: 0px;
+ left: 0px;
+ opacity: 0;
+ transition: all 0.2s linear;
+ text-align: center;
+}
+
+img.camera-progress {
+ margin-top: 28%;
+ border: 10px solid white;
+ border-radius: 10px;
+}
+
/* media queries */
applyButton.css('display', 'inline-block');
applyButton.animate({'opacity': '1'}, 100);
applyButton.addClass('progress');
+
+ $('img.camera').css('opacity', '0.3');
+ $('div.camera-progress').css('opacity', '0.5');
}
function hideApply() {
else {
showApply();
}
+
+ $('div.camera-progress').css('opacity', '0');
+ $('img.camera').css('opacity', '1');
}
function isProgress() {
for (var i = 0; i < configs.length; i++) {
var config = configs[i];
+ if (i === configs.length - 1) {
+ config.config['restart'] = true;
+ }
ajax('POST', '/config/' + config.key + '/set/', config.config, function () {
finishedCount++;
testReady();
'<div class="camera-container">' +
'<div class="camera-placeholder"></div>' +
'<img class="camera">' +
+ '<div class="camera-progress"><img class="camera-progress"></div>' +
'</div>' +
'</div>');
var configureButton = cameraFrameDiv.find('div.camera-button.configure');
var closeButton = cameraFrameDiv.find('div.camera-button.close');
var cameraImg = cameraFrameDiv.find('img.camera');
+ var progressImg = cameraFrameDiv.find('img.camera-progress');
cameraFrameDiv.attr('id', 'camera' + cameraId);
cameraFrameDiv[0].framerate = framerate;
cameraFrameDiv[0].refreshDivider = 0;
nameSpan.html(cameraName);
+ progressImg.attr('src', staticUrl + 'img/camera-progress.gif');
/* insert the new camera frame at the right position,
* with respect to the camera id */