주변 블루투스 스캔을 추가
This commit is contained in:
+106
-1
@@ -16,6 +16,7 @@
|
||||
wifi5: $('#wifi5'),
|
||||
wifiTable: $('#wifiTable'),
|
||||
wifiScan: $('#wifiScan'),
|
||||
bluetoothScan: $('#bluetoothScan'),
|
||||
statusHost: $('#statusHost'),
|
||||
statusLoad: $('#statusLoad'),
|
||||
statusUsers: $('#statusUsers'),
|
||||
@@ -153,6 +154,18 @@
|
||||
wifiScanFresh: 'fresh',
|
||||
wifiScanDisabledWithHistory: 'scan disabled, showing last observed networks',
|
||||
wifiScanObservedAll: 'all observed BSSID history',
|
||||
bluetoothScan: 'Nearby Bluetooth Scan',
|
||||
bluetoothScanDisabled: 'Nearby Bluetooth scan is disabled.',
|
||||
bluetoothScanEmpty: 'No nearby Bluetooth scan results.',
|
||||
bluetoothScanCached: 'cached',
|
||||
bluetoothScanFresh: 'fresh',
|
||||
bluetoothScanDisabledWithHistory: 'scan disabled, showing last observed devices',
|
||||
bluetoothScanObservedAll: 'all observed devices',
|
||||
deviceName: 'Device',
|
||||
address: 'Address',
|
||||
paired: 'Paired',
|
||||
trusted: 'Trusted',
|
||||
blocked: 'Blocked',
|
||||
observed: 'Observed',
|
||||
ssid: 'SSID',
|
||||
channel: 'Channel',
|
||||
@@ -306,6 +319,18 @@
|
||||
wifiScanFresh: '실시간',
|
||||
wifiScanDisabledWithHistory: '스캔 꺼짐, 마지막 관측 목록 표시',
|
||||
wifiScanObservedAll: '전체 BSSID 누적 관측',
|
||||
bluetoothScan: '주변 Bluetooth 스캔',
|
||||
bluetoothScanDisabled: '주변 Bluetooth 스캔이 꺼져 있습니다.',
|
||||
bluetoothScanEmpty: '주변 Bluetooth 스캔 결과가 없습니다.',
|
||||
bluetoothScanCached: '캐시',
|
||||
bluetoothScanFresh: '실시간',
|
||||
bluetoothScanDisabledWithHistory: '스캔 꺼짐, 마지막 관측 장치 표시',
|
||||
bluetoothScanObservedAll: '전체 장치 누적 관측',
|
||||
deviceName: '장치',
|
||||
address: '주소',
|
||||
paired: '페어링',
|
||||
trusted: '신뢰',
|
||||
blocked: '차단',
|
||||
observed: '관측',
|
||||
ssid: 'SSID',
|
||||
channel: '채널',
|
||||
@@ -1329,6 +1354,7 @@
|
||||
}
|
||||
|
||||
renderWifiScan(data.wifi?.scan || null);
|
||||
renderBluetoothScan(data.bluetooth?.scan || null);
|
||||
}
|
||||
|
||||
function renderWifiScan(scan) {
|
||||
@@ -1342,7 +1368,7 @@
|
||||
|
||||
const networks = Array.isArray(scan?.networks) ? scan.networks : [];
|
||||
if (!scan || (scan.enabled === false && networks.length === 0)) {
|
||||
els.wifiScan.innerHTML = `<div class="wifi-scan-empty">${escapeHtml(t('wifiScanDisabled'))}</div>`;
|
||||
els.wifiScan.innerHTML = `<div class="scan-empty">${escapeHtml(t('wifiScanDisabled'))}</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1404,6 +1430,85 @@
|
||||
});
|
||||
}
|
||||
|
||||
function renderBluetoothScan(scan) {
|
||||
if (!els.bluetoothScan) return;
|
||||
const wrap = els.bluetoothScan.querySelector('.table-wrap');
|
||||
const scrollLeft = wrap ? wrap.scrollLeft : 0;
|
||||
const devices = Array.isArray(scan?.devices) ? scan.devices : [];
|
||||
|
||||
if (!scan || (scan.enabled === false && devices.length === 0)) {
|
||||
els.bluetoothScan.innerHTML = `<div class="scan-empty">${escapeHtml(t('bluetoothScanDisabled'))}</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
const observedMode = scan.observed_mode === 'all' ? t('bluetoothScanObservedAll') : '-';
|
||||
const status = scan.enabled === false
|
||||
? `${t('bluetoothScanDisabledWithHistory')} · ${observedMode}`
|
||||
: `${scan.cached ? t('bluetoothScanCached') : t('bluetoothScanFresh')} · ${escapeHtml(scan.generated_at || '-')} · ${observedMode}`;
|
||||
const rows = devices.length ? devices.map(device => `
|
||||
<tr>
|
||||
<td>${escapeHtml(device.display_name || device.alias || device.name || '-')}</td>
|
||||
<td>${escapeHtml(device.address || '-')}</td>
|
||||
<td>${escapeHtml(device.address_type || '-')}</td>
|
||||
<td><span class="wifi-signal ${bluetoothSignalClass(device.rssi)}">${escapeHtml(bluetoothRssiText(device.rssi))}</span></td>
|
||||
<td>${escapeHtml(device.tx_power ?? '-')}</td>
|
||||
<td>${escapeHtml(booleanStateText(device.connected))}</td>
|
||||
<td>${escapeHtml(booleanStateText(device.paired))}</td>
|
||||
<td>${escapeHtml(booleanStateText(device.trusted))}</td>
|
||||
<td>${escapeHtml(booleanStateText(device.blocked))}</td>
|
||||
<td>${escapeHtml(device.icon || '-')}</td>
|
||||
<td>${escapeHtml(device.class || '-')}</td>
|
||||
<td>${escapeHtml(joinDetailLines(device.uuids))}</td>
|
||||
<td>${escapeHtml(joinDetailLines(device.manufacturer_data))}</td>
|
||||
<td>${escapeHtml(joinDetailLines(device.service_data))}</td>
|
||||
<td>${escapeHtml(device.modalias || '-')}</td>
|
||||
<td>${escapeHtml(device.appearance || '-')}</td>
|
||||
<td>${escapeHtml(wifiObservedText(device))}</td>
|
||||
</tr>
|
||||
`).join('') : `<tr><td colspan="17">${escapeHtml(t('bluetoothScanEmpty'))}</td></tr>`;
|
||||
|
||||
els.bluetoothScan.innerHTML = `
|
||||
<div class="wifi-scan-head">
|
||||
<span>${status}</span>
|
||||
</div>
|
||||
<div class="table-wrap compact">
|
||||
<table class="wifi-scan-table">
|
||||
<thead><tr><th>${escapeHtml(t('deviceName'))}</th><th>${escapeHtml(t('address'))}</th><th>주소 종류</th><th>RSSI</th><th>TxPower</th><th>${escapeHtml(t('connected'))}</th><th>${escapeHtml(t('paired'))}</th><th>${escapeHtml(t('trusted'))}</th><th>${escapeHtml(t('blocked'))}</th><th>Icon</th><th>Class</th><th>UUID</th><th>제조사 데이터</th><th>서비스 데이터</th><th>Modalias</th><th>Appearance</th><th>${escapeHtml(t('observed'))}</th></tr></thead>
|
||||
<tbody>${rows}</tbody>
|
||||
</table>
|
||||
</div>
|
||||
`;
|
||||
requestAnimationFrame(() => {
|
||||
const nextWrap = els.bluetoothScan.querySelector('.table-wrap');
|
||||
if (nextWrap) nextWrap.scrollLeft = scrollLeft;
|
||||
});
|
||||
}
|
||||
|
||||
function bluetoothRssiText(value) {
|
||||
return Number.isFinite(Number(value)) ? `${Number(value)} dBm` : '-';
|
||||
}
|
||||
|
||||
function bluetoothSignalClass(value) {
|
||||
const dbm = Number(value);
|
||||
if (!Number.isFinite(dbm)) return 'unknown';
|
||||
if (dbm >= -55) return 'excellent';
|
||||
if (dbm >= -67) return 'good';
|
||||
if (dbm >= -75) return 'fair';
|
||||
if (dbm >= -85) return 'weak';
|
||||
return 'bad';
|
||||
}
|
||||
|
||||
function booleanStateText(value) {
|
||||
if (value === true) return state.lang === 'ko' ? '예' : 'yes';
|
||||
if (value === false) return state.lang === 'ko' ? '아니오' : 'no';
|
||||
return '-';
|
||||
}
|
||||
|
||||
function joinDetailLines(value) {
|
||||
if (!Array.isArray(value) || value.length === 0) return '-';
|
||||
return value.join(' / ');
|
||||
}
|
||||
|
||||
function wifiObservedText(row) {
|
||||
if (row?.observed_source === 'live') {
|
||||
return row?.last_seen_at ? `${timeAgo(secondsSinceLocalTime(row.last_seen_at))} · ${row.last_seen_at}` : '방금';
|
||||
|
||||
Reference in New Issue
Block a user