--> disk usage at bottom
-> make camera frames positions configurable
-> add a view log functionality
-> add a previewer for snapshots
from collections import OrderedDict
import settings
+import utils
import v4l2ctl
return data
+
def camera_dict_to_ui(data):
if data['@proto'] == 'v4l2':
device_uri = data['videodevice']
+ disk_used, disk_total = utils.get_disk_usage(data['target_dir'])
else:
device_uri = '%(host)s:%(port)s/config/%(camera_id)s' % {
'host': data['@host'],
'port': data['@port'],
'camera_id': data['@remote_camera_id']}
+
+ disk_used, disk_total = data['disk_used'], data['disk_total']
ui = {
# device
'network_username': data['@network_username'],
'network_password': data['@network_password'],
'root_directory': data.get('target_dir'),
+ 'disk_used': disk_used,
+ 'disk_total': disk_total,
# text overlay
'text_overlay': False,
tmp_config = config.camera_ui_to_dict(remote_ui_config)
tmp_config.update(camera_config)
+ tmp_config['disk_used'] = remote_ui_config['disk_used']
+ tmp_config['disk_total'] = remote_ui_config['disk_total']
ui_config = config.camera_dict_to_ui(tmp_config)
ui_config['available_resolutions'] = remote_ui_config['available_resolutions']
motionctl.restart()
self.finish_json()
-
+
+
class SnapshotHandler(BaseHandler):
@asynchronous
def get(self, camera_id, op, filename=None):
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import datetime
+import logging
+import os
def pretty_date_time(date_time, tzinfo=None):
format = '-' + format
return format.format(d=days, h=hours, m=minutes, s=seconds)
+
+
+def get_disk_usage(path):
+ logging.debug('getting disk usage for path %(path)s...' % {
+ 'path': path})
+
+ try:
+ result = os.statvfs(path)
+
+ except OSError as e:
+ logging.error('failed to execute statvfs: %(msg)s' % {'msg': unicode(e)})
+
+ return None
+
+ block_size = result.f_frsize
+ free_blocks = result.f_bfree
+ total_blocks = result.f_blocks
+
+ free_size = free_blocks * block_size
+ total_size = total_blocks * block_size
+ used_size = total_size - free_size
+
+ return (used_size, total_size)
position: absolute;
bottom: 5px;
width: 100%;
- height: 2.2em;
+ height: 3em;
font-size: 0.7em;
color: #aaa;
text-align: center;
}
+div.copyright-note {
+ border-top: 1px solid #333;
+ padding-top: 0.2em;
+ margin: 0px 15%;
+}
+
div.page-container {
transition: all 0.2s linear;
- padding: 55px 5px 2.2em 5px;
+ padding: 55px 5px 3em 5px;
}
div.page-container.stretched {
width: 50px;
}
+span.disk-usage-text {
+ vertical-align: middle;
+}
+
+div.disk-usage-bar-container {
+ position: relative;
+ width: 90%;
+ height: 1em;
+ border: 1px solid #555;
+ vertical-align: middle;
+ margin: 0px 0.2em;
+ text-align: center;
+ line-height: 1em;
+}
+
+div.disk-usage-bar-fill {
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ bottom: 0px;
+ width: 0%;
+ background-color: #555;
+}
+
+span.disk-usage-percent {
+ font-size: 0.8em;
+ position: relative;
+}
+
div.rpi,
tr.rpi {
display: none;
$('#networkUsernameEntry').val(dict['network_username']);
$('#networkPasswordEntry').val(dict['network_password']);
$('#rootDirectoryEntry').val(dict['root_directory']);
+ var percent = parseInt(dict['disk_used'] * 100 / dict['disk_total']);
+ $('#diskUsageBarFill').css('width', percent + '%');
+ $('#diskUsageText').html(
+ (dict['disk_used'] / 1073741824).toFixed(1) + '/' + (dict['disk_total'] / 1073741824).toFixed(1) + ' GB (' + percent + '%)');
/* text overlay */
$('#textOverlaySwitch')[0].checked = dict['text_overlay'];
var modalWidth = container.width();
var modalHeight = container.height();
- container.css('left', (windowWidth - modalWidth) / 2);
- container.css('top', (windowHeight - modalHeight) / 2);
+ container.css('left', Math.floor((windowWidth - modalWidth) / 2));
+ container.css('top', Math.floor((windowHeight - modalHeight) / 2));
}
function makeModalDialogButtons(buttonsInfo) {
<td class="settings-item-value"><input type="text" class="styled storage" id="rootDirectoryEntry"></td>
<td><span class="help-mark" title="the root path (on the selected storage device) where the files will be saved">?</span></td>
</tr>
+ <tr class="settings-item advanced-setting">
+ <td colspan="100"><div class="settings-item-separator"></div></td>
+ </tr>
+ <tr class="settings-item advanced-setting">
+ <td class="settings-item-label"><span class="settings-item-label">Disk Usage</span></td>
+ <td class="settings-item-value">
+ <div class="disk-usage-bar-container">
+ <div class="disk-usage-bar-fill" id="diskUsageBarFill"></div>
+ <span class="disk-usage-percent" id="diskUsageText"></span>
+ </div>
+ </td>
+ <td><span class="help-mark" title="the used/total size of the disk where the root directory resides">?</span></td>
+ </tr>
</table>
<div class="settings-section-title advanced-setting"><input type="checkbox" class="styled section text-overlay" id="textOverlaySwitch">Text Overlay</div>
<img class="main-loading-progress" src="{{STATIC_URL}}img/main-loading-progress.gif">
</div>
<div class="footer">
- <div class="system-info">free space: 100MB/300MB</div>
<div class="copyright-note">copyright © Calin Crisan 2013</div>
</div>
</div>