Refine DHCP client display

This commit is contained in:
seo
2026-07-20 23:32:21 +09:00
parent 2261d3ebe4
commit 34bb82b750
4 changed files with 115 additions and 25 deletions
+87 -4
View File
@@ -2713,9 +2713,29 @@ function lan_fdb_macs(string $iface = 'eth1'): array
return array_keys($macs);
}
function lan_link_speed_label(string $iface = 'eth1'): string
{
$speed = trim((string)@file_get_contents('/sys/class/net/' . $iface . '/speed'));
if ($speed === '' || !ctype_digit($speed) || (int)$speed <= 0) {
return '-';
}
$mbps = (int)$speed;
if ($mbps >= 1000 && $mbps % 1000 === 0) {
return (int)($mbps / 1000) . ' Gbps';
}
if ($mbps >= 1000) {
return rtrim(rtrim(number_format($mbps / 1000, 1), '0'), '.') . ' Gbps';
}
return $mbps . ' Mbps';
}
function lan_client_rows(array $leases, array $aliases, string $iface = 'eth1'): array
{
$rows = [];
$linkSpeed = lan_link_speed_label($iface);
foreach (lan_fdb_macs($iface) as $mac) {
$lease = $leases[$mac] ?? [];
@@ -2733,10 +2753,10 @@ function lan_client_rows(array $leases, array $aliases, string $iface = 'eth1'):
'hostname_source' => $hostnameSource,
'name' => $hostname === 'N/A' ? $mac : $hostname,
'signal' => '-',
'tx_bitrate' => '-',
'tx_bitrate' => $linkSpeed,
'rx_bitrate' => '-',
'signal_source' => 'lan',
'tx_bitrate_source' => 'lan',
'tx_bitrate_source' => 'lan_link',
'rx_bitrate_source' => 'lan',
'connected_time' => 'N/A',
'inactive_time' => '-',
@@ -2903,6 +2923,42 @@ function wifi_connected_time_missing(mixed $value): bool
return $text === '' || $text === 'N/A' || $text === '-';
}
function wifi_connected_seconds(mixed $value): ?float
{
$text = strtolower(trim((string)$value));
if ($text === '' || $text === 'n/a' || $text === '-') {
return null;
}
if (is_numeric($text)) {
return (float)$text;
}
$total = 0.0;
$matched = false;
if (preg_match_all('/([0-9]+(?:\.[0-9]+)?)\s*(days?|d|hours?|hrs?|h|minutes?|mins?|min|m|seconds?|secs?|sec|s|milliseconds?|msecs?|msec|ms)\b/i', $text, $matches, PREG_SET_ORDER)) {
foreach ($matches as $match) {
$amount = (float)$match[1];
$unit = strtolower($match[2]);
$matched = true;
if ($unit === 'd' || str_starts_with($unit, 'day')) {
$total += $amount * 86400;
} elseif ($unit === 'h' || str_starts_with($unit, 'hour') || str_starts_with($unit, 'hr')) {
$total += $amount * 3600;
} elseif ($unit === 'm' || str_starts_with($unit, 'min')) {
$total += $amount * 60;
} elseif ($unit === 'ms' || str_starts_with($unit, 'msec') || str_starts_with($unit, 'millisecond')) {
$total += $amount / 1000;
} else {
$total += $amount;
}
}
}
return $matched ? $total : null;
}
function wifi_valid_mac(string $mac): bool
{
return preg_match('/^[0-9a-f]{2}(?::[0-9a-f]{2}){5}$/', strtolower($mac)) === 1;
@@ -2996,7 +3052,8 @@ function observed_wifi_duration_seconds(array $clients): array
]);
}
$placeholders = implode(',', array_fill(0, count($targets), '?'));
$targetMacs = array_values(array_unique(array_column($targets, 'mac')));
$placeholders = implode(',', array_fill(0, count($targetMacs), '?'));
$stmt = $pdo->prepare("
SELECT
mac,
@@ -3004,7 +3061,7 @@ function observed_wifi_duration_seconds(array $clients): array
FROM wifi_observed_sessions
WHERE mac IN ($placeholders)
");
$stmt->execute(array_values(array_unique(array_column($targets, 'mac'))));
$stmt->execute($targetMacs);
$rows = $stmt->fetchAll();
$pdo->commit();
@@ -3050,6 +3107,31 @@ function apply_observed_wifi_connected_time(array $clients): array
return $clients;
}
function sort_clients_by_connected_time(array $clients): array
{
usort($clients, static function (array $a, array $b): int {
$aSeconds = wifi_connected_seconds($a['connected_time'] ?? null);
$bSeconds = wifi_connected_seconds($b['connected_time'] ?? null);
if ($aSeconds === null && $bSeconds === null) {
return strcmp((string)($a['band'] ?? ''), (string)($b['band'] ?? ''))
?: strcmp((string)($a['hostname'] ?? ''), (string)($b['hostname'] ?? ''));
}
if ($aSeconds === null) {
return 1;
}
if ($bSeconds === null) {
return -1;
}
return $bSeconds <=> $aSeconds;
});
return $clients;
}
function wifi_data(): array
{
$leases = dnsmasq_leases();
@@ -3072,6 +3154,7 @@ function wifi_data(): array
$clients = dedupe_wifi_clients($clients);
$clients = apply_wifi_estimates($clients);
$clients = apply_observed_wifi_connected_time($clients);
$clients = sort_clients_by_connected_time($clients);
return [
'clients' => $clients,
+21 -14
View File
@@ -108,10 +108,10 @@
fanStatus: 'Fan Status',
cpuTemp: 'CPU Temp',
fanRpm: 'Fan RPM',
wifiStatus: 'WiFi Status',
clients24: '2.4G Clients',
clients5: '5G Clients',
clientsLan: 'LAN Clients',
wifiStatus: 'DHCP Status',
clients24: '2.4G DHCP',
clients5: '5G DHCP',
clientsLan: 'LAN DHCP',
systemStatus: 'System Status',
loadAvg: 'Load Avg',
activeUsers: 'Active Users',
@@ -152,7 +152,7 @@
cpuWatt: 'CPU Watt',
remaining: 'Remaining',
batteryVoltage: 'Battery Voltage',
wifiClients: 'WiFi Clients',
wifiClients: 'DHCP Clients',
deviceName: 'Device',
address: 'Address',
paired: 'Paired',
@@ -201,7 +201,7 @@
dialogInput: 'Input',
dialogOk: 'OK',
dialogCancel: 'Cancel',
noWifiClients: 'No connected WiFi clients',
noWifiClients: 'No connected DHCP clients',
noProcessActivity: 'No process activity',
noCause: 'No candidate',
recordedReason: 'Recorded reason',
@@ -256,10 +256,10 @@
fanStatus: '팬 상태',
cpuTemp: 'CPU 온도',
fanRpm: '팬 RPM',
wifiStatus: 'WiFi 상태',
clients24: '2.4G 접속',
clients5: '5G 접속',
clientsLan: 'LAN 접속',
wifiStatus: 'DHCP 상태',
clients24: '2.4G DHCP',
clients5: '5G DHCP',
clientsLan: 'LAN DHCP',
systemStatus: '시스템 상태',
loadAvg: '부하 평균',
activeUsers: '활성 사용자',
@@ -300,7 +300,7 @@
cpuWatt: 'CPU 전력',
remaining: '잔여 시간',
batteryVoltage: '배터리 전압',
wifiClients: 'WiFi 클라이언트',
wifiClients: 'DHCP 클라이언트',
deviceName: '장치',
address: '주소',
paired: '페어링',
@@ -349,7 +349,7 @@
dialogInput: '입력',
dialogOk: '확인',
dialogCancel: '취소',
noWifiClients: '연결된 WiFi 클라이언트 없음',
noWifiClients: '연결된 DHCP 클라이언트 없음',
noProcessActivity: '프로세스 활동 없음',
noCause: '원인 후보 없음',
recordedReason: '기록된 이유',
@@ -1224,6 +1224,14 @@
return `<td>${signalBadgeHtml(dbm, row[key] || '', `client:${row.mac || row.ip || row.hostname || ''}`)}</td>`;
}
function wifiRateCells(row) {
if (row?.band === 'LAN') {
return `<td colspan="2">${escapeHtml(wifiCellValue(row, 'tx_bitrate'))}</td>`;
}
return `${wifiMetricCell(row, 'tx_bitrate')}${wifiMetricCell(row, 'rx_bitrate')}`;
}
function wifiSignalDbm(value) {
const match = String(value ?? '').match(/-?\d+(?:\.\d+)?/);
return match ? Number(match[0]) : null;
@@ -1430,8 +1438,7 @@
${td(row.ip)}
${td(row.mac)}
${wifiMetricCell(row, 'signal')}
${wifiMetricCell(row, 'tx_bitrate')}
${wifiMetricCell(row, 'rx_bitrate')}
${wifiRateCells(row)}
${td(wifiConnectedCellValue(row))}
${td(wifiCellValue(row, 'inactive_time'))}
</tr>
+5 -5
View File
@@ -199,11 +199,11 @@ html[data-theme="light"] .details-panel{background:#edf3fa;border-color:#b8c7da}
</div>
<div class="card">
<h2 data-i18n="wifiStatus">WiFi Status</h2>
<h2 data-i18n="wifiStatus">DHCP Status</h2>
<div class="stat-grid">
<div class="stat"><div class="stat-label" data-i18n="clients24">2.4G Clients</div><div class="stat-value" id="wifi24">-</div></div>
<div class="stat"><div class="stat-label" data-i18n="clients5">5G Clients</div><div class="stat-value" id="wifi5">-</div></div>
<div class="stat"><div class="stat-label" data-i18n="clientsLan">LAN Clients</div><div class="stat-value" id="wifiLan">-</div></div>
<div class="stat"><div class="stat-label" data-i18n="clients24">2.4G DHCP</div><div class="stat-value" id="wifi24">-</div></div>
<div class="stat"><div class="stat-label" data-i18n="clients5">5G DHCP</div><div class="stat-value" id="wifi5">-</div></div>
<div class="stat"><div class="stat-label" data-i18n="clientsLan">LAN DHCP</div><div class="stat-value" id="wifiLan">-</div></div>
</div>
</div>
@@ -249,7 +249,7 @@ html[data-theme="light"] .details-panel{background:#edf3fa;border-color:#b8c7da}
</div>
<div class="card">
<h2 data-i18n="wifiClients">WiFi Clients</h2>
<h2 data-i18n="wifiClients">DHCP Clients</h2>
<div class="table-wrap">
<table class="wifi-table">
<colgroup>