WiFi 표시값과 잔여 시간 설명 강화

This commit is contained in:
seo
2026-06-26 02:36:30 +09:00
parent a4d8c775e9
commit 4f102efc4b
5 changed files with 316 additions and 20 deletions
+81 -4
View File
@@ -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("'", '&#039;');
}
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);