WiFi 표시값과 잔여 시간 설명 강화
This commit is contained in:
+81
-4
@@ -156,6 +156,9 @@
|
||||
serviceEnabled: 'enabled',
|
||||
servicePid: 'pid',
|
||||
serviceRestarts: 'restarts',
|
||||
wifiAliasTitle: 'WiFi Hostname',
|
||||
wifiAliasPrompt: 'Enter a name to keep for this MAC address.',
|
||||
wifiAliasSaved: 'WiFi hostname saved.',
|
||||
notifySuccess: 'success',
|
||||
notifyFailed: 'failed',
|
||||
show: 'Show',
|
||||
@@ -287,6 +290,9 @@
|
||||
serviceEnabled: '활성화',
|
||||
servicePid: 'PID',
|
||||
serviceRestarts: '재시작',
|
||||
wifiAliasTitle: 'WiFi 호스트명',
|
||||
wifiAliasPrompt: '이 MAC 주소에 계속 표시할 이름을 입력하세요.',
|
||||
wifiAliasSaved: 'WiFi 호스트명을 저장했습니다.',
|
||||
notifySuccess: '성공',
|
||||
notifyFailed: '실패',
|
||||
show: '보기',
|
||||
@@ -691,6 +697,9 @@
|
||||
};
|
||||
const lines = [sourceMap[remaining.source] || remaining.source || '-'];
|
||||
|
||||
if (remaining.seconds !== undefined && remaining.seconds !== null) {
|
||||
lines.push(`계산 잔여: ${formatDurationSeconds(remaining.seconds)}`);
|
||||
}
|
||||
if (remaining.drop_per_hour !== undefined && remaining.drop_per_hour !== null) {
|
||||
lines.push(`시간당 SOC 감소: ${Number(remaining.drop_per_hour).toFixed(3)}%`);
|
||||
}
|
||||
@@ -716,6 +725,16 @@
|
||||
}
|
||||
return `${row.minutes}분`;
|
||||
}).join(', ')}`);
|
||||
lines.push('');
|
||||
lines.push('세부 후보');
|
||||
remaining.windows.forEach(row => {
|
||||
if (row.source === 'learned_profile') {
|
||||
lines.push(`- 장기 학습: ${row.intervals || 0}구간 / SOC대 ${row.soc_buckets || 0}개 / 누적감소 ${Number(row.drop_percent || 0).toFixed(3)}% / 신뢰도 ${(Number(row.confidence || 0) * 100).toFixed(0)}%`);
|
||||
} else {
|
||||
const load = row.load_factor === null || row.load_factor === undefined ? '-' : `${Number(row.load_factor).toFixed(3)}x`;
|
||||
lines.push(`- 최근 ${row.minutes}분: 감소 ${Number(row.drop_percent || 0).toFixed(3)}% / 신뢰도 ${(Number(row.confidence || 0) * 100).toFixed(0)}% / 부하보정 ${load}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
@@ -802,6 +821,59 @@
|
||||
return `<td>${escapeHtml(v)}</td>`;
|
||||
}
|
||||
|
||||
function attr(v) {
|
||||
return escapeHtml(v).replaceAll("'", ''');
|
||||
}
|
||||
|
||||
function wifiValueTitle(row, key) {
|
||||
const source = row?.[`${key}_source`];
|
||||
if (!source || source === 'iw' || source === 'lease') return '';
|
||||
const labels = {
|
||||
alias: 'DB 저장 이름',
|
||||
generated: 'MAC 기반 임시 이름',
|
||||
estimated_from_rate: '속도 기반 추정',
|
||||
estimated_from_signal: '신호 기반 추정',
|
||||
estimated_from_rx: '수신 속도 기반 추정',
|
||||
estimated_from_tx: '송신 속도 기반 추정',
|
||||
estimated_default: '대역 기본 추정',
|
||||
};
|
||||
return labels[source] || source;
|
||||
}
|
||||
|
||||
function wifiHostCell(row) {
|
||||
const title = wifiValueTitle(row, 'hostname') || t('wifiAliasPrompt');
|
||||
return `<td><button type="button" class="wifi-host-edit" data-mac="${attr(row.mac || '')}" data-hostname="${attr(row.hostname || '')}" title="${attr(title)}">${escapeHtml(row.hostname || '-')}</button></td>`;
|
||||
}
|
||||
|
||||
function wifiMetricCell(row, key) {
|
||||
const title = wifiValueTitle(row, key);
|
||||
const className = title ? ' class="wifi-estimated"' : '';
|
||||
const titleAttr = title ? ` title="${attr(title)}"` : '';
|
||||
return `<td${className}${titleAttr}>${escapeHtml(row[key] || '-')}</td>`;
|
||||
}
|
||||
|
||||
async function editWifiHostname(mac, currentName) {
|
||||
if (!mac) return;
|
||||
const value = await window.customPrompt(`${t('wifiAliasPrompt')}\n${mac}`, {
|
||||
title: t('wifiAliasTitle'),
|
||||
defaultValue: currentName || '',
|
||||
okText: t('dialogOk'),
|
||||
});
|
||||
if (value === null) return;
|
||||
|
||||
const hostname = String(value || '').trim();
|
||||
if (!hostname) return;
|
||||
|
||||
try {
|
||||
const data = await api('wifi_alias', { mac, hostname });
|
||||
notice(t('wifiAliasSaved'), 'success');
|
||||
render(data.status || await api('status'));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
notice(e.message || 'WiFi alias failed', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function parseWifiDurationSeconds(value) {
|
||||
const text = String(value ?? '').trim();
|
||||
if (text === '' || text.toUpperCase() === 'N/A') {
|
||||
@@ -930,12 +1002,12 @@
|
||||
? rows.map(row => `
|
||||
<tr>
|
||||
${td(row.band)}
|
||||
${td(row.hostname)}
|
||||
${wifiHostCell(row)}
|
||||
${td(row.ip)}
|
||||
${td(row.mac)}
|
||||
${td(row.signal)}
|
||||
${td(row.tx_bitrate)}
|
||||
${td(row.rx_bitrate)}
|
||||
${wifiMetricCell(row, 'signal')}
|
||||
${wifiMetricCell(row, 'tx_bitrate')}
|
||||
${wifiMetricCell(row, 'rx_bitrate')}
|
||||
${td(wifiConnectedTime(row.connected_time))}
|
||||
${td(row.inactive_time)}
|
||||
</tr>
|
||||
@@ -1744,6 +1816,11 @@
|
||||
els.dmesgToggle?.addEventListener('click', () => {
|
||||
setDmesgOpen(!state.dmesgOpen);
|
||||
});
|
||||
els.wifiTable?.addEventListener('click', event => {
|
||||
const button = event.target?.closest?.('.wifi-host-edit');
|
||||
if (!button) return;
|
||||
editWifiHostname(button.dataset.mac || '', button.dataset.hostname || '');
|
||||
});
|
||||
els.statusBatteryRemaining?.addEventListener('mouseenter', showBatteryTooltip);
|
||||
els.statusBatteryRemaining?.addEventListener('mouseleave', hideBatteryTooltip);
|
||||
els.statusBatteryRemaining?.addEventListener('focus', showBatteryTooltip);
|
||||
|
||||
Reference in New Issue
Block a user