Admin angepasst

This commit is contained in:
2026-05-28 08:37:14 +02:00
parent abfc8be412
commit 7a9e0ae62e
3 changed files with 506 additions and 64 deletions
+212 -48
View File
@@ -154,42 +154,162 @@
<span class="p-lbl">Systemstatus</span>
<span class="p-tag">Telemetry</span>
</div>
<div class="ss-hdr"><div class="ss-hdr-line"></div><span class="ss-hdr-txt">Netzwerk</span><div class="ss-hdr-line"></div></div>
<div class="sr">
<span class="sr-led led-ok"></span>
<span class="sr-key">WLAN</span>
<span class="sr-val" id="info_wifi">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Signal</span>
<span class="sr-val" id="info_wifi_signal">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">IP</span>
<span class="sr-val" id="info_ips">-</span>
</div>
<div class="ss-hdr"><div class="ss-hdr-line"></div><span class="ss-hdr-txt">Hardware</span><div class="ss-hdr-line"></div></div>
<div class="sr sr-gauge gauge-temp">
<span class="sr-key">Temp</span>
<div class="gauge"><div class="gauge-fill" id="bar-temp-adm"></div></div>
<span class="gauge-num" id="info_temp">--</span>
</div>
<div class="sr">
<span class="sr-led led-off"></span>
<span class="sr-key">Spannung</span>
<span class="sr-val" id="info_undervoltage">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Uptime</span>
<span class="sr-val" id="info_uptime">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Disk frei</span>
<span class="sr-val" id="info_disk">-</span>
<div class="systemstatus-grid">
<div class="systemstatus-section">
<div class="ss-hdr"><div class="ss-hdr-line"></div><span class="ss-hdr-txt">Netzwerk</span><div class="ss-hdr-line"></div></div>
<div class="sr">
<span class="sr-led led-ok"></span>
<span class="sr-key">WLAN</span>
<span class="sr-val" id="info_wifi">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">SSID</span>
<span class="sr-val" id="info_wifi_ssid">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Signal</span>
<span class="sr-val" id="info_wifi_signal">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Signal dBm</span>
<span class="sr-val" id="info_wifi_signal_dbm">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Kanal</span>
<span class="sr-val" id="info_wifi_channel">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Frequenz</span>
<span class="sr-val" id="info_wifi_frequency">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">IP</span>
<span class="sr-val" id="info_ips">-</span>
</div>
</div>
<div class="systemstatus-section">
<div class="ss-hdr"><div class="ss-hdr-line"></div><span class="ss-hdr-txt">System</span><div class="ss-hdr-line"></div></div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Pi-Modell</span>
<span class="sr-val" id="info_pi_model">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Hostname</span>
<span class="sr-val" id="info_hostname">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Kernel</span>
<span class="sr-val" id="info_kernel">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Pi-Zeit</span>
<span class="sr-val" id="info_system_time">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Zeitzone</span>
<span class="sr-val" id="info_system_timezone">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Zeit-Sync</span>
<span class="sr-val" id="info_time_sync">-</span>
</div>
</div>
<div class="systemstatus-section">
<div class="ss-hdr"><div class="ss-hdr-line"></div><span class="ss-hdr-txt">Hardware</span><div class="ss-hdr-line"></div></div>
<div class="sr sr-gauge gauge-temp">
<span class="sr-key">Temp</span>
<div class="gauge"><div class="gauge-fill" id="bar-temp-adm"></div></div>
<span class="gauge-num" id="info_temp">--</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">GPU-/SoC-Temp</span>
<span class="sr-val" id="info_gpu_temp">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">CPU gesamt</span>
<span class="sr-val" id="info_cpu_usage">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">CPU Kerne</span>
<span class="sr-val" id="info_cpu_per_core">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">RAM</span>
<span class="sr-val" id="info_memory_details">-</span>
</div>
<div class="sr">
<span class="sr-led led-off"></span>
<span class="sr-key">Spannung</span>
<span class="sr-val" id="info_undervoltage">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Throttling</span>
<span class="sr-val" id="info_throttling">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Uptime</span>
<span class="sr-val" id="info_uptime">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Load 1/5/15</span>
<span class="sr-val" id="info_load_average">-</span>
</div>
</div>
<div class="systemstatus-section">
<div class="ss-hdr"><div class="ss-hdr-line"></div><span class="ss-hdr-txt">Speicher & Container</span><div class="ss-hdr-line"></div></div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Root-Dateisystem</span>
<span class="sr-val" id="info_disk_root">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Docker-Speicher</span>
<span class="sr-val" id="info_disk_docker">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Inodes frei</span>
<span class="sr-val" id="info_inode_free">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Container-Uptime</span>
<span class="sr-val" id="info_container_uptime">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Container-Restarts</span>
<span class="sr-val" id="info_container_restarts">-</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Disk frei</span>
<span class="sr-val" id="info_disk">-</span>
</div>
</div>
</div>
</div>
</div>
@@ -784,10 +904,31 @@
function clearAdminApiDisplay() {
setText('info_ips', '--');
setText('info_wifi', '--');
setText('info_wifi_ssid', '--');
setText('info_wifi_signal', '--');
setText('info_wifi_signal_dbm', '--');
setText('info_wifi_channel', '--');
setText('info_wifi_frequency', '--');
setText('info_pi_model', '--');
setText('info_hostname', '--');
setText('info_kernel', '--');
setText('info_system_time', '--');
setText('info_system_timezone', '--');
setText('info_time_sync', '--');
setText('info_temp', '--');
setText('info_gpu_temp', '--');
setText('info_cpu_usage', '--');
setText('info_cpu_per_core', '--');
setText('info_memory_details', '--');
setText('info_uptime', '--');
setText('info_disk', '--');
setText('info_disk_root', '--');
setText('info_disk_docker', '--');
setText('info_inode_free', '--');
setText('info_load_average', '--');
setText('info_throttling', '--');
setText('info_container_uptime', '--');
setText('info_container_restarts', '--');
setUndervoltageState('info_undervoltage', '--', 'unknown');
setServiceState('service_camera', '-');
setServiceState('camera_service_state', '-');
@@ -2285,20 +2426,43 @@
}
function renderStatus(data) {
var system = data.system || {};
var services = data.services || {};
setAdminStatus(data.status || 'Bereit');
setText('info_ips', data.system && data.system.ips);
setText('info_wifi', data.system && data.system.wifi);
setText('info_wifi_signal', data.system && data.system.wifi_signal);
setText('info_temp', data.system && data.system.cpu_temperature);
setText('info_ips', system.ips);
setText('info_wifi', system.wifi);
setText('info_wifi_ssid', system.wifi_ssid);
setText('info_wifi_signal', system.wifi_signal);
setText('info_wifi_signal_dbm', system.wifi_signal_dbm);
setText('info_wifi_channel', system.wifi_channel);
setText('info_wifi_frequency', system.wifi_frequency);
setText('info_pi_model', system.pi_model);
setText('info_hostname', system.hostname);
setText('info_kernel', system.kernel_version);
setText('info_system_time', system.system_time);
setText('info_system_timezone', system.system_timezone);
setText('info_time_sync', 'NTP: ' + (system.ntp_sync || '--') + ' | Uhr: ' + (system.clock_sync || '--'));
setText('info_temp', system.cpu_temperature);
setText('info_gpu_temp', system.gpu_temperature);
setText('info_cpu_usage', system.cpu_usage);
setText('info_cpu_per_core', system.cpu_usage_per_core);
setText('info_memory_details', system.memory_details || system.memory_usage);
var bar = document.getElementById('bar-temp-adm');
if (bar && data.system) bar.style.width = temperatureToWidth(data.system.cpu_temperature);
setUndervoltageState('info_undervoltage', data.system && data.system.undervoltage, data.system && data.system.undervoltage_state);
setText('info_uptime', data.system && data.system.uptime);
setText('info_disk', data.system && data.system.disk_free);
setServiceState('service_camera', data.services && data.services.camera);
setServiceState('camera_service_state', data.services && data.services.camera);
setServiceState('service_admin_api', data.services && data.services.admin_api);
setServiceState('service_exomy', data.services && data.services.exomy);
if (bar) bar.style.width = temperatureToWidth(system.cpu_temperature);
setUndervoltageState('info_undervoltage', system.undervoltage, system.undervoltage_state);
setText('info_throttling', system.throttling_details);
setText('info_uptime', system.uptime);
setText('info_load_average', system.load_average);
setText('info_disk', system.disk_free);
setText('info_disk_root', system.disk_root);
setText('info_disk_docker', system.disk_docker);
setText('info_inode_free', system.inode_free);
setText('info_container_uptime', services.exomy_uptime);
setText('info_container_restarts', services.exomy_restarts);
setServiceState('service_camera', services.camera);
setServiceState('camera_service_state', services.camera);
setServiceState('service_admin_api', services.admin_api);
setServiceState('service_exomy', services.exomy);
}
async function fetchStatus() {
+19
View File
@@ -975,6 +975,24 @@ body.page-admin .ss-hdr{
}
body.page-admin .ss-hdr-line{ flex:1; height:1px; background:repeating-linear-gradient(90deg, rgba(0,255,145,.22) 0, rgba(0,255,145,.22) 3px, transparent 3px, transparent 6px); }
body.page-admin .ss-hdr-txt{ font-size:7px; letter-spacing:4px; color:rgba(210,255,235,.9); text-transform:uppercase; }
body.page-admin .systemstatus-grid{
display:grid;
grid-template-columns:repeat(2, minmax(0, 1fr));
gap:12px;
padding:12px;
}
body.page-admin .systemstatus-section{
min-width:0;
border:1px solid rgba(0,255,145,.08);
background:rgba(0,255,145,.025);
}
body.page-admin .systemstatus-section .ss-hdr{
margin:0;
border-top:none;
}
body.page-admin .systemstatus-section .sr:last-child{
border-bottom:none;
}
/* ── GAUGE ──────────────────────────────────────── */
body.page-admin .sr-gauge{ padding-bottom:6px; }
@@ -1595,6 +1613,7 @@ body.page-admin .leaflet-popup-tip{
body.page-admin .drive-estimator-value{ font-size:28px; }
body.page-admin .drive-calibration-toolbar{ flex-direction:column; align-items:stretch; }
body.page-admin .drive-calibration-grid{ grid-template-columns:1fr; }
body.page-admin .systemstatus-grid{ grid-template-columns:1fr; }
body.page-admin .admin-section-grid_map{ grid-template-columns:1fr; }
body.page-admin .admin-section-grid_gps{ grid-template-columns:1fr; }
body.page-admin .map-panel{ min-height:0; }
+275 -16
View File
@@ -7,6 +7,7 @@ import http.server
import json
import os
import shutil
import socket
import socketserver
import struct
import subprocess
@@ -85,6 +86,14 @@ def read_cpu_temperature():
return 'unbekannt'
def read_gpu_temperature():
stdout, _, returncode = run_command(['vcgencmd', 'measure_temp'])
if returncode != 0 or not stdout or '=' not in stdout:
return 'unbekannt'
value = stdout.split('=', 1)[1].strip().replace("'C", ' °C')
return value or 'unbekannt'
def read_cpu_usage():
try:
with open('/proc/stat', 'r', encoding='utf-8') as handle:
@@ -108,6 +117,41 @@ def read_cpu_usage():
return 'unbekannt'
def read_per_core_usage():
try:
with open('/proc/stat', 'r', encoding='utf-8') as handle:
first_lines = [line.split() for line in handle if line.startswith('cpu') and line[3:4].isdigit()]
time.sleep(0.12)
with open('/proc/stat', 'r', encoding='utf-8') as handle:
second_lines = [line.split() for line in handle if line.startswith('cpu') and line[3:4].isdigit()]
except (FileNotFoundError, ValueError):
return 'unbekannt'
if len(first_lines) != len(second_lines) or not first_lines:
return 'unbekannt'
results = []
for first, second in zip(first_lines, second_lines):
try:
name = first[0]
first_values = [int(value) for value in first[1:]]
second_values = [int(value) for value in second[1:]]
idle_first = first_values[3] + first_values[4]
idle_second = second_values[3] + second_values[4]
total_first = sum(first_values)
total_second = sum(second_values)
total_delta = total_second - total_first
idle_delta = idle_second - idle_first
if total_delta <= 0:
continue
usage = (1 - (idle_delta / total_delta)) * 100
results.append(f'{name.upper()}: {usage:.0f}%')
except (ValueError, IndexError):
continue
return ' | '.join(results) if results else 'unbekannt'
def read_memory_usage():
try:
values = {}
@@ -126,6 +170,27 @@ def read_memory_usage():
return 'unbekannt'
def read_memory_details():
try:
values = {}
with open('/proc/meminfo', 'r', encoding='utf-8') as handle:
for line in handle:
key, raw_value = line.split(':', 1)
values[key] = int(raw_value.strip().split()[0])
total = values.get('MemTotal')
available = values.get('MemAvailable')
if not total or available is None:
return 'unbekannt'
used = total - available
total_mb = total / 1024
used_mb = used / 1024
free_mb = available / 1024
percent = (used / total) * 100
return f'{used_mb:.0f} MB belegt / {free_mb:.0f} MB frei / {total_mb:.0f} MB gesamt ({percent:.0f}%)'
except (FileNotFoundError, ValueError):
return 'unbekannt'
def read_undervoltage_status():
stdout, _, returncode = run_command(['vcgencmd', 'get_throttled'])
if returncode != 0 or not stdout or '=' not in stdout:
@@ -161,6 +226,32 @@ def read_undervoltage_status():
}
def read_throttling_details():
stdout, _, returncode = run_command(['vcgencmd', 'get_throttled'])
if returncode != 0 or not stdout or '=' not in stdout:
return 'unbekannt'
try:
throttled_value = int(stdout.split('=', 1)[1].strip(), 16)
except ValueError:
return 'unbekannt'
flags = [
(0x1, 'Unterspannung aktuell'),
(0x2, 'ARM gedrosselt aktuell'),
(0x4, 'aktuell limitiert'),
(0x8, 'Soft-Temperaturlimit aktuell'),
(0x10000, 'Unterspannung früher'),
(0x20000, 'ARM früher gedrosselt'),
(0x40000, 'früher limitiert'),
(0x80000, 'Soft-Temperaturlimit früher'),
]
active = [label for bitmask, label in flags if throttled_value & bitmask]
if not active:
return 'keine Auffälligkeiten'
return ' | '.join(active)
def format_uptime():
try:
with open('/proc/uptime', 'r', encoding='utf-8') as handle:
@@ -188,6 +279,39 @@ def format_disk_free():
return f'{free_gb:.1f} GB frei / {total_gb:.1f} GB'
def format_path_disk_usage(path):
try:
usage = shutil.disk_usage(path)
free_gb = usage.free / (1024 ** 3)
used_gb = usage.used / (1024 ** 3)
total_gb = usage.total / (1024 ** 3)
return f'{used_gb:.1f} GB belegt / {free_gb:.1f} GB frei / {total_gb:.1f} GB'
except FileNotFoundError:
return 'unbekannt'
def read_inode_free(path='/'):
try:
stats = os.statvfs(path)
total = stats.f_files
free = stats.f_ffree
used = total - free
if total <= 0:
return 'unbekannt'
percent_used = (used / total) * 100
return f'{free:,} frei / {total:,} gesamt ({percent_used:.0f}% belegt)'.replace(',', '.')
except OSError:
return 'unbekannt'
def read_load_average():
try:
load_1, load_5, load_15 = os.getloadavg()
return f'{load_1:.2f} / {load_5:.2f} / {load_15:.2f}'
except (AttributeError, OSError):
return 'unbekannt'
def get_ip_addresses():
stdout, _, returncode = run_command(['ip', '-4', '-o', 'addr', 'show', 'dev', 'wlan0', 'scope', 'global'])
if returncode == 0 and stdout:
@@ -235,29 +359,87 @@ def get_wifi_status():
return 'kein wlan0'
def get_wifi_signal():
stdout, _, returncode = run_command(['nmcli', '-t', '-f', 'GENERAL.STATE,AP.SIGNAL', 'device', 'show', 'wlan0'])
def get_wifi_details():
details = {
'ssid': 'unbekannt',
'signal_percent': 'unbekannt',
'signal_dbm': 'unbekannt',
'channel': 'unbekannt',
'frequency': 'unbekannt',
}
stdout, _, returncode = run_command(['nmcli', '-t', '-f', 'ACTIVE,SSID,SIGNAL,CHAN,FREQ,BARS', 'device', 'wifi', 'list', 'ifname', 'wlan0'])
if returncode != 0 or not stdout:
return 'unbekannt'
return details
state = ''
signal = ''
for line in stdout.splitlines():
if line.startswith('GENERAL.STATE:'):
state = line.split(':', 1)[1].strip()
elif line.startswith('AP[1].SIGNAL:'):
signal = line.split(':', 1)[1].strip()
if not line.startswith('yes:'):
continue
parts = line.split(':')
if len(parts) >= 6:
ssid = parts[1].strip()
signal = parts[2].strip()
channel = parts[3].strip()
frequency = parts[4].strip()
details['ssid'] = ssid or 'unbekannt'
details['signal_percent'] = f'{signal} %' if signal else 'unbekannt'
details['channel'] = channel or 'unbekannt'
details['frequency'] = frequency or 'unbekannt'
try:
dbm = (int(float(signal)) / 2) - 100
details['signal_dbm'] = f'{dbm:.0f} dBm'
except ValueError:
details['signal_dbm'] = 'unbekannt'
break
return details
if 'connected' not in state.lower():
return 'nicht verbunden'
if not signal:
return 'unbekannt'
def get_wifi_signal():
details = get_wifi_details()
return details['signal_percent']
def get_hostname():
try:
percent = max(0, min(100, int(float(signal))))
except ValueError:
return socket.gethostname()
except Exception:
return 'unbekannt'
return f'{percent} %'
def get_kernel_version():
stdout, _, returncode = run_command(['uname', '-r'])
if returncode == 0 and stdout:
return stdout
return 'unbekannt'
def get_pi_model():
try:
with open('/proc/device-tree/model', 'r', encoding='utf-8') as handle:
return handle.read().strip('\x00').strip() or 'unbekannt'
except (FileNotFoundError, ValueError):
return 'unbekannt'
def get_system_time_details():
local_time = datetime.datetime.now().astimezone()
timedatectl_stdout, _, timedatectl_returncode = run_command(['timedatectl', 'show', '-p', 'NTPSynchronized', '-p', 'SystemClockSynchronized', '-p', 'Timezone'])
ntp_sync = 'unbekannt'
clock_sync = 'unbekannt'
timezone_name = local_time.tzname() or 'unbekannt'
if timedatectl_returncode == 0 and timedatectl_stdout:
for line in timedatectl_stdout.splitlines():
if line.startswith('NTPSynchronized='):
ntp_sync = 'ja' if line.split('=', 1)[1].strip().lower() == 'yes' else 'nein'
elif line.startswith('SystemClockSynchronized='):
clock_sync = 'ja' if line.split('=', 1)[1].strip().lower() == 'yes' else 'nein'
elif line.startswith('Timezone='):
timezone_name = line.split('=', 1)[1].strip() or timezone_name
return {
'local': local_time.strftime('%Y-%m-%d %H:%M:%S'),
'timezone': timezone_name,
'ntp_sync': ntp_sync,
'clock_sync': clock_sync,
}
def get_systemd_state(service_name):
@@ -274,6 +456,59 @@ def get_container_state(container_name):
return 'unknown'
def get_container_details(container_name):
stdout, _, returncode = run_command(['docker', 'inspect', container_name])
if returncode != 0 or not stdout:
return {
'uptime': 'unbekannt',
'restarts': 'unbekannt',
'docker_disk': 'unbekannt',
}
try:
payload = json.loads(stdout)
info = payload[0] if payload else {}
state = info.get('State', {})
started_at = state.get('StartedAt')
restart_count = info.get('RestartCount', state.get('RestartCount', 'unbekannt'))
uptime = 'unbekannt'
if started_at and started_at != '0001-01-01T00:00:00Z':
started = datetime.datetime.fromisoformat(started_at.replace('Z', '+00:00'))
delta = datetime.datetime.now(datetime.timezone.utc) - started
uptime = format_duration(int(delta.total_seconds()))
return {
'uptime': uptime,
'restarts': str(restart_count),
'docker_disk': get_docker_disk_usage(),
}
except (ValueError, KeyError, TypeError, IndexError):
return {
'uptime': 'unbekannt',
'restarts': 'unbekannt',
'docker_disk': 'unbekannt',
}
def get_docker_disk_usage():
stdout, _, returncode = run_command(['docker', 'info', '-f', '{{.DockerRootDir}}'])
if returncode != 0 or not stdout:
return 'unbekannt'
return format_path_disk_usage(stdout.strip())
def format_duration(seconds):
days, remainder = divmod(max(0, int(seconds)), 86400)
hours, remainder = divmod(remainder, 3600)
minutes, _ = divmod(remainder, 60)
parts = []
if days:
parts.append(f'{days}d')
if days or hours:
parts.append(f'{hours}h')
parts.append(f'{minutes}m')
return ' '.join(parts)
def get_motor_test_container_status():
state = get_container_state(EXOMY_CONTAINER)
return {
@@ -285,25 +520,49 @@ def get_motor_test_container_status():
def collect_status():
undervoltage = read_undervoltage_status()
wifi_details = get_wifi_details()
time_details = get_system_time_details()
container_details = get_container_details(EXOMY_CONTAINER)
return {
'status': 'Bereit',
'system': {
'wifi': get_wifi_status(),
'wifi_signal': get_wifi_signal(),
'wifi_signal_dbm': wifi_details['signal_dbm'],
'wifi_ssid': wifi_details['ssid'],
'wifi_channel': wifi_details['channel'],
'wifi_frequency': wifi_details['frequency'],
'ips': get_ip_addresses(),
'undervoltage': undervoltage['text'],
'undervoltage_state': undervoltage['state'],
'throttling_details': read_throttling_details(),
'cpu_temperature': read_cpu_temperature(),
'gpu_temperature': read_gpu_temperature(),
'cpu_usage': read_cpu_usage(),
'cpu_usage_per_core': read_per_core_usage(),
'memory_usage': read_memory_usage(),
'memory_details': read_memory_details(),
'uptime': format_uptime(),
'disk_free': format_disk_free(),
'disk_root': format_path_disk_usage('/'),
'disk_docker': container_details['docker_disk'],
'inode_free': read_inode_free('/'),
'load_average': read_load_average(),
'hostname': get_hostname(),
'kernel_version': get_kernel_version(),
'pi_model': get_pi_model(),
'system_time': time_details['local'],
'system_timezone': time_details['timezone'],
'ntp_sync': time_details['ntp_sync'],
'clock_sync': time_details['clock_sync'],
},
'services': {
'camera': get_systemd_state('exomy-camera-stream.service'),
'camera_settings_version': read_camera_settings_version(),
'admin_api': get_systemd_state('exomy-admin-api.service'),
'exomy': get_container_state(EXOMY_CONTAINER),
'exomy_uptime': container_details['uptime'],
'exomy_restarts': container_details['restarts'],
}
}