_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']
_main_config_cache = None
_camera_config_cache = {}
extra_options.append((name, value))
ui['extra_options'] = extra_options
+
+ # action commands
+ action_commands = get_action_commands(data['@id'])
+ ui['actions'] = action_commands.keys()
return ui
return ui
+def get_action_commands(camera_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
+
+ return action_commands
+
+
def backup():
logging.debug('generating config backup file')
raise HTTPError(400, 'unknown operation')
+class ActionHandler(BaseHandler):
+ @asynchronous
+ def post(self, camera_id, action):
+ camera_id = int(camera_id)
+ if camera_id not in config.get_camera_ids():
+ raise HTTPError(404, 'no such camera')
+
+ if action == 'snapshot':
+ logging.debug('executing snapshot action for camera with id %s' % camera_id)
+ return self.snapshot()
+
+ elif action == 'record_start':
+ logging.debug('executing record_start action for camera with id %s' % camera_id)
+ return self.record_start()
+
+ elif action == 'record_stop':
+ logging.debug('executing record_stop action for camera with id %s' % camera_id)
+ return self.record_stop()
+
+ action_commands = config.get_action_commands(camera_id)
+ command = action_commands.get(action)
+ if not command:
+ raise HTTPError(400, 'unknown action')
+
+ logging.debug('executing %s action for camera with id %s: "%s"' % (action, camera_id, command))
+ self.run_command_bg(command)
+
+ def run_command_bg(self, command):
+ self.p = subprocess.Popen(command, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
+ self.command = command
+
+ self.io_loop = IOLoop.instance()
+ self.io_loop.add_timeout(datetime.timedelta(milliseconds=100), self.check_command)
+
+ def check_command(self):
+ exit_status = self.p.poll()
+ if exit_status is not None:
+ output = self.p.stdout.read()
+ lines = output.split('\n')
+ if not lines[-1]:
+ lines = lines[:-1]
+ command = os.path.basename(self.command)
+ if exit_status:
+ logging.warn('%s: command has finished with non-zero exit status: %s' % (command, exit_status))
+ for line in lines:
+ logging.warn('%s: %s' % (command, line))
+
+ else:
+ logging.debug('%s: command has finished' % command)
+ for line in lines:
+ logging.debug('%s: %s' % (command, line))
+
+ self.finish_json({'status': exit_status})
+
+ else:
+ self.io_loop.add_timeout(datetime.timedelta(milliseconds=100), self.check_command)
+
+
class PrefsHandler(BaseHandler):
def get(self, key):
self.finish_json(self.get_pref(key))
(r'^/movie/(?P<camera_id>\d+)/(?P<op>list)/?$', handlers.MovieHandler),
(r'^/movie/(?P<camera_id>\d+)/(?P<op>download|preview|delete)/(?P<filename>.+?)/?$', handlers.MovieHandler),
(r'^/movie/(?P<camera_id>\d+)/(?P<op>delete_all)/(?P<group>.*?)/?$', handlers.MovieHandler),
+ (r'^/action/(?P<camera_id>\d+)/(?P<action>\w+)/?$', handlers.ActionHandler),
(r'^/prefs/(?P<key>\w+)/?$', handlers.PrefsHandler),
(r'^/_relay_event/?$', handlers.RelayEventHandler),
(r'^/log/(?P<name>\w+)/?$', handlers.LogHandler),
div.camera-frame:HOVER div.camera-overlay-top,
div.camera-frame:HOVER div.camera-overlay-bottom {
- background-color: rgba(65, 65, 65, 0.8);
+ background-color: rgba(40, 40, 40, 0.8);
}
div.camera-frame.motion-detected div.camera-overlay-top,
vertical-align: top;
}
+div.camera-action-button.pending {
+ opacity: 0.5 !important;
+}
+
div.camera-action-button.lock {
background-position: 0px 0px;
}
}, {stack: true});
}
+function doAction(cameraId, action, callback) {
+ ajax('POST', basePath + 'action/' + cameraId + '/' + action + '/', null, function (data) {
+ if (data == null || data.error) {
+ showErrorMessage(data && data.error);
+ }
+
+ if (callback) {
+ callback();
+ }
+ });
+}
+
/* fetch & push */
var alarmOnButton = cameraFrameDiv.find('div.camera-action-button.alarm-on');
var alarmOffButton = cameraFrameDiv.find('div.camera-action-button.alarm-off');
var snapshotButton = cameraFrameDiv.find('div.camera-action-button.snapshot');
- var recordButton = cameraFrameDiv.find('div.camera-action-button.record');
+ var recordButton = cameraFrameDiv.find('div.camera-action-button.record-start');
var cameraOverlay = cameraFrameDiv.find('div.camera-overlay');
var cameraPlaceholder = cameraFrameDiv.find('div.camera-placeholder');
/* fade in */
cameraFrameDiv.animate({'opacity': 1}, 100);
- /* add the top button handlers */
+ /* add the top buttons handlers */
configureButton.click(function () {
doConfigureCamera(cameraId);
});
};
}(cameraId));
- /* add the action button handlers */
-// if (cameraConfig.at-most-4-buttons) { TODO
-// cameraOverlay.find('div.camera-overlay-bottom').addClass('few-buttons');
-// }
- //TODO add handlers
+ /* action buttons */
+
+ cameraFrameDiv.find('div.camera-action-button').css('display', 'none');
+ var actionButtonDict = {
+ 'lock': lockButton,
+ 'unlock': unlockButton,
+ 'light_on': lightOnButton,
+ 'light_off': lightOffButton,
+ 'alarm_on': alarmOnButton,
+ 'alarm_off': alarmOffButton,
+ 'snapshpt': snapshotButton,
+ 'record': recordButton
+ };
+ cameraConfig.actions.forEach(function (action) {
+ var button = actionButtonDict[action];
+ if (!button) {
+ return;
+ }
+
+ button.css('display', '');
+ button.click(function () {
+ if (button.hasClass('pending')) {
+ return;
+ }
+
+ button.addClass('pending');
+
+ if (action == 'record') {
+ if (button.hasClass('record-start')) {
+ action = 'record_start';
+ }
+ else {
+ action = 'record_stop';
+ }
+ }
+
+ doAction(cameraId, action, function () {
+ button.removeClass('pending');
+ });
+ })
+ });
+
+ if (cameraConfig.actions.length <= 4) {
+ cameraOverlay.find('div.camera-overlay-bottom').addClass('few-buttons');
+ }
+
var FPS_LEN = 4;
cameraImg[0].fpsTimes = [];
else {
cameraFrameDiv.removeClass('motion-detected');
}
+
+ if (getCookie('record_active_' + cameraId) == 'true') {
+ recordButton.removeClass('record-start').addClass('record-stop');
+ }
+ else {
+ recordButton.removeClass('record-stop').addClass('record-start');
+ }
this.lastCookieTime = now;