From: Calin Crisan Date: Fri, 10 Jun 2016 18:28:41 +0000 (+0300) Subject: added support for monitoring commands X-Git-Url: http://www.vanbest.org/gitweb/?a=commitdiff_plain;h=ba70fd8b378e7043fa50f7cb2fc71c7cb0fe3d2e;p=motioneye-debian added support for monitoring commands --- diff --git a/motioneye/config.py b/motioneye/config.py index 92b8b20..d686a36 100644 --- a/motioneye/config.py +++ b/motioneye/config.py @@ -47,6 +47,7 @@ _camera_ids_cache = None _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') @@ -249,8 +250,6 @@ def get_network_shares(): 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] @@ -349,8 +348,6 @@ def get_camera(camera_id, as_lines=False): def set_camera(camera_id, camera_config): - global _camera_config_cache - camera_config['@id'] = camera_id _camera_config_cache[camera_id] = camera_config @@ -453,7 +450,6 @@ def set_camera(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']: @@ -538,7 +534,7 @@ def add_camera(device_details): set_camera(camera_id, camera_config) _camera_ids_cache = None - _camera_config_cache = {} + _camera_config_cache.clear() camera_config = get_camera(camera_id) @@ -547,7 +543,6 @@ def add_camera(device_details): 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} @@ -564,7 +559,7 @@ def rem_camera(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) @@ -1388,6 +1383,22 @@ def get_action_commands(camera_id): 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') diff --git a/motioneye/handlers.py b/motioneye/handlers.py index 63159a8..ac1644e 100644 --- a/motioneye/handlers.py +++ b/motioneye/handlers.py @@ -29,6 +29,7 @@ from tornado.web import RequestHandler, HTTPError, asynchronous import config import mediafiles import mjpgclient +import monitor import motionctl import powerctl import prefs @@ -212,18 +213,20 @@ class MainHandler(BaseHandler): 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) @@ -864,23 +867,29 @@ class PictureHandler(BaseHandler): 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) diff --git a/motioneye/monitor.py b/motioneye/monitor.py new file mode 100644 index 0000000..6612507 --- /dev/null +++ b/motioneye/monitor.py @@ -0,0 +1,67 @@ + +# 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 . + +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 diff --git a/motioneye/remote.py b/motioneye/remote.py index d2ddb78..23323c7 100644 --- a/motioneye/remote.py +++ b/motioneye/remote.py @@ -320,8 +320,9 @@ def get_current_picture(local_config, width, height, callback): 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' % { @@ -331,7 +332,7 @@ def get_current_picture(local_config, width, height, callback): 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)) diff --git a/motioneye/static/css/main.css b/motioneye/static/css/main.css index 2ee7404..7922d0f 100644 --- a/motioneye/static/css/main.css +++ b/motioneye/static/css/main.css @@ -981,6 +981,9 @@ div.camera-overlay-bottom.few-buttons { 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; @@ -989,12 +992,32 @@ div.camera-info { 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 { diff --git a/motioneye/static/js/main.js b/motioneye/static/js/main.js index 0a92b00..b273a24 100644 --- a/motioneye/static/js/main.js +++ b/motioneye/static/js/main.js @@ -3878,7 +3878,7 @@ function addCameraFrameUi(cameraConfig) { '' + '
' + '
' + - '' + + '' + '
' + '
' + '
' + @@ -3902,8 +3902,9 @@ function addCameraFrameUi(cameraConfig) { 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'); @@ -4067,7 +4068,7 @@ function addCameraFrameUi(cameraConfig) { cameraPlaceholder.css('opacity', 1); cameraProgress.removeClass('visible'); cameraFrameDiv.removeClass('motion-detected'); - fpsSpan.html(''); + cameraInfoSpan.html(''); }; cameraImg[0].onload = function () { if (this.error) { @@ -4106,6 +4107,7 @@ function addCameraFrameUi(cameraConfig) { } var captureFps = getCookie('capture_fps_' + cameraId); + var monitorInfo = getCookie('monitor_info_' + cameraId); this.lastCookieTime = now; @@ -4113,14 +4115,25 @@ function addCameraFrameUi(cameraConfig) { 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 += '
' + monitorInfo; + cameraInfoDiv.addClass('two-lines'); + } + else { + cameraInfoDiv.removeClass('two-lines') + } - fpsSpan.html(fps); + cameraInfoSpan.html(info); } }