_additional_section_funcs = []
_additional_config_funcs = []
_additional_structure_cache = {}
+_monitor_command_cache = {}
# starting with r490 motion config directives have changed a bit
_LAST_OLD_CONFIG_VERSIONS = (490, '3.2.12')
def get_camera(camera_id, as_lines=False):
- global _camera_config_cache
-
if not as_lines and camera_id in _camera_config_cache:
return _camera_config_cache[camera_id]
def set_camera(camera_id, camera_config):
- global _camera_config_cache
-
camera_config['@id'] = camera_id
_camera_config_cache[camera_id] = camera_config
def add_camera(device_details):
global _camera_ids_cache
- global _camera_config_cache
proto = device_details['proto']
if proto in ['netcam', 'mjpeg']:
set_camera(camera_id, camera_config)
_camera_ids_cache = None
- _camera_config_cache = {}
+ _camera_config_cache.clear()
camera_config = get_camera(camera_id)
def rem_camera(camera_id):
global _camera_ids_cache
- global _camera_config_cache
camera_config_name = _CAMERA_CONFIG_FILE_NAME % {'id': camera_id}
camera_config_path = os.path.join(settings.CONF_PATH, _CAMERA_CONFIG_FILE_NAME) % {'id': camera_id}
logging.info('removing camera config file %(path)s...' % {'path': camera_config_path})
_camera_ids_cache = None
- _camera_config_cache = {}
+ _camera_config_cache.clear()
try:
os.remove(camera_config_path)
return action_commands
+def get_monitor_command(camera_id):
+ if camera_id not in _monitor_command_cache:
+ path = os.path.join(settings.CONF_PATH, 'monitor_%s' % camera_id)
+ if os.access(path, os.X_OK):
+ _monitor_command_cache[camera_id] = path
+
+ else:
+ _monitor_command_cache[camera_id] = None
+
+ return _monitor_command_cache[camera_id]
+
+
+def invalidate_monitor_commands():
+ _monitor_command_cache.clear()
+
+
def backup():
logging.debug('generating config backup file')
import config
import mediafiles
import mjpgclient
+import monitor
import motionctl
import powerctl
import prefs
class ConfigHandler(BaseHandler):
@asynchronous
def get(self, camera_id=None, op=None):
+ config.invalidate_monitor_commands()
+
if camera_id is not None:
camera_id = int(camera_id)
-
+
if op == 'get':
self.get_config(camera_id)
-
+
elif op == 'list':
self.list()
-
+
elif op == 'backup':
self.backup()
-
+
elif op == 'authorize':
self.authorize(camera_id)
width = width and float(width)
height = height and float(height)
+ camera_id_str = str(camera_id)
+
camera_config = config.get_camera(camera_id)
if utils.local_motion_camera(camera_config):
picture = mediafiles.get_current_picture(camera_config,
width=width,
height=height)
- self.set_cookie('motion_detected_' + str(camera_id), str(motionctl.is_motion_detected(camera_id)).lower())
- self.set_cookie('capture_fps_' + str(camera_id), '%.1f' % mjpgclient.get_fps(camera_id))
+ self.set_cookie('motion_detected_' + camera_id_str, str(motionctl.is_motion_detected(camera_id)).lower())
+ self.set_cookie('capture_fps_' + camera_id_str, '%.1f' % mjpgclient.get_fps(camera_id))
+ self.set_cookie('monitor_info_' + camera_id_str, monitor.get_monitor_info(camera_id))
+
self.try_finish(picture)
elif utils.remote_camera(camera_config):
- def on_response(motion_detected=False, fps=None, picture=None, error=None):
+ def on_response(motion_detected=False, capture_fps=None, monitor_info=None, picture=None, error=None):
if error:
return self.try_finish(None)
- self.set_cookie('motion_detected_' + str(camera_id), str(motion_detected).lower())
- self.set_cookie('capture_fps_' + str(camera_id), '%.1f' % fps)
+ self.set_cookie('motion_detected_' + camera_id_str, str(motion_detected).lower())
+ self.set_cookie('capture_fps_' + camera_id_str, '%.1f' % capture_fps)
+ self.set_cookie('monitor_info_' + camera_id_str, monitor_info or '')
+
self.try_finish(picture)
remote.get_current_picture(camera_config, width=width, height=height, callback=on_response)
--- /dev/null
+
+# Copyright (c) 2013 Calin Crisan
+# This file is part of motionEye.
+#
+# motionEye is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import logging
+import re
+import subprocess
+import time
+
+import config
+
+
+DEFAULT_INTERVAL = 1 # seconds
+
+_monior_info_cache_by_camera_id = {}
+_last_call_time_by_camera_id = {}
+_interval_by_camera_id = {}
+
+
+def get_monitor_info(camera_id):
+ global _command_cache_time
+
+ now = time.time()
+ command = config.get_monitor_command(camera_id)
+ if command is None:
+ return ''
+
+ monitor_info = _monior_info_cache_by_camera_id.get(camera_id)
+ last_call_time = _last_call_time_by_camera_id.get(camera_id, 0)
+ interval = _interval_by_camera_id.get(camera_id, DEFAULT_INTERVAL)
+ if monitor_info is None or now - last_call_time > interval:
+ monitor_info, interval = _exec_monitor_command(command)
+ monitor_info = re.sub('[\x00-\x20]', ' ', monitor_info)
+ _interval_by_camera_id[camera_id] = interval
+ _monior_info_cache_by_camera_id[camera_id] = monitor_info
+ _last_call_time_by_camera_id[camera_id] = now
+
+ return monitor_info
+
+
+def _exec_monitor_command(command):
+ process = subprocess.Popen([command], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ out, err = process.communicate()
+
+ try:
+ interval = int(err)
+
+ except:
+ interval = DEFAULT_INTERVAL
+
+ out = out.strip()
+ logging.debug('monitoring command "%s" returned "%s"' % (command, out))
+
+ return out, interval
def on_response(response):
cookies = utils.parse_cookies(response.headers.get_list('Set-Cookie'))
motion_detected = cookies.get('motion_detected_' + str(camera_id)) == 'true'
- fps = cookies.get('capture_fps_' + str(camera_id))
- fps = float(fps) if fps else 0
+ capture_fps = cookies.get('capture_fps_' + str(camera_id))
+ capture_fps = float(capture_fps) if capture_fps else 0
+ monitor_info = cookies.get('monitor_info_' + str(camera_id))
if response.error:
logging.error('failed to get current picture for remote camera %(id)s on %(url)s: %(msg)s' % {
return callback(error=utils.pretty_http_error(response))
- callback(motion_detected, fps, response.body)
+ callback(motion_detected, capture_fps, monitor_info, response.body)
http_client = AsyncHTTPClient()
http_client.fetch(request, _callback_wrapper(on_response))
div.camera-info {
display: inline-block;
white-space: nowrap;
+ font-family: monospace;
+ font-size: 1em;
+ font-weight: bold;
width: 40%;
overflow: hidden;
text-overflow: ellipsis;
line-height: 5em;
}
+div.camera-info.two-lines {
+ line-height: 1.2em;
+}
+
div.camera-overlay-bottom.few-buttons div.camera-info {
line-height: 2.5em;
}
+div.camera-overlay-bottom.few-buttons div.camera-info.two-lines {
+ line-height: 1.2em;
+ font-size: 0.8em;
+}
+
span.camera-info {
- padding-left: 0.5em;
+ display: inline-block;
+ margin-left: 1em;
+}
+
+div.camera-info.two-lines > span.camera-info {
+ margin-left: 1em;
+ margin-top: 1.5em;
+}
+
+div.camera-overlay-bottom.few-buttons div.camera-info.two-lines > span.camera-info {
+ margin-left: 0.5em;
+ margin-top: 0.5em;
}
div.camera-action-buttons {
'</div>' +
'<div class="camera-overlay-bottom">' +
'<div class="camera-info">' +
- '<span class="camera-info fps" title="streaming/capture frame rate"></span>' +
+ '<span class="camera-info" title="streaming/capture frame rate"></span>' +
'</div>' +
'<div class="camera-action-buttons">' +
'<div class="camera-action-buttons-wrapper">' +
var picturesButton = cameraFrameDiv.find('div.camera-top-button.media-pictures');
var moviesButton = cameraFrameDiv.find('div.camera-top-button.media-movies');
var fullScreenButton = cameraFrameDiv.find('div.camera-top-button.full-screen');
-
- var fpsSpan = cameraFrameDiv.find('span.camera-info.fps');
+
+ var cameraInfoDiv = cameraFrameDiv.find('div.camera-info');
+ var cameraInfoSpan = cameraFrameDiv.find('span.camera-info');
var lockButton = cameraFrameDiv.find('div.camera-action-button.lock');
var unlockButton = cameraFrameDiv.find('div.camera-action-button.unlock');
cameraPlaceholder.css('opacity', 1);
cameraProgress.removeClass('visible');
cameraFrameDiv.removeClass('motion-detected');
- fpsSpan.html('');
+ cameraInfoSpan.html('');
};
cameraImg[0].onload = function () {
if (this.error) {
}
var captureFps = getCookie('capture_fps_' + cameraId);
+ var monitorInfo = getCookie('monitor_info_' + cameraId);
this.lastCookieTime = now;
var streamingFps = this.fpsTimes.length * 1000 / (this.fpsTimes[this.fpsTimes.length - 1] - this.fpsTimes[0]);
streamingFps = streamingFps.toFixed(1);
- var fps = streamingFps;
+ var info = streamingFps;
if (captureFps) {
- fps += '/' + captureFps;
+ info += '/' + captureFps;
}
- fps += ' fps';
+ info += ' fps';
+
+ if (monitorInfo) {
+ if (monitorInfo.charAt(0) == monitorInfo.charAt(monitorInfo.length - 1)) {
+ monitorInfo = monitorInfo.substring(1, monitorInfo.length - 1);
+ }
+ info += '<br>' + monitorInfo;
+ cameraInfoDiv.addClass('two-lines');
+ }
+ else {
+ cameraInfoDiv.removeClass('two-lines')
+ }
- fpsSpan.html(fps);
+ cameraInfoSpan.html(info);
}
}