Control 알림과 notice 설정 세분화

This commit is contained in:
seo
2026-06-26 03:21:01 +09:00
parent 7a9d4af9ad
commit da8c4e863d
4 changed files with 84 additions and 32 deletions
+33 -15
View File
@@ -1720,7 +1720,7 @@ function sync_current_battery_remaining_history(array $history, array $currentRo
}
$history[] = $currentRow;
return array_slice($history, -240);
return array_slice($history, -(int)setting_value('battery.history_sync_limit'));
}
function process_service_name(int $pid): string
@@ -1973,7 +1973,8 @@ function fan_spike_analysis(array $history, array $fan, array $system, array $pr
];
}
$rpmExpected = $mode !== 'off' && ($pwm >= 20 || $pwmAvg >= 20);
$rpmExpectedPwm = (float)setting_value('notice.rpm_expected_pwm');
$rpmExpected = $mode !== 'off' && ($pwm >= $rpmExpectedPwm || $pwmAvg >= $rpmExpectedPwm);
$reasons = [];
$rpmThreshold = (float)setting_value('notice.rpm_delta_threshold');
@@ -1984,11 +1985,12 @@ function fan_spike_analysis(array $history, array $fan, array $system, array $pr
if ($rpmExpected && abs($rpmDelta) >= $rpmThreshold) $reasons[] = 'RPM ' . signed_delta_text($rpmDelta);
if (abs($tempDelta) >= $tempThreshold) $reasons[] = 'TEMP ' . signed_delta_text($tempDelta, 'C');
$spiking = false;
$alertStarted = false;
if ($currentState === 'normal') {
$spiking = $reasons !== [];
if ($spiking) {
$alertStarted = $reasons !== [];
if ($alertStarted) {
$currentState = 'alert';
save_system_notice_state(
'alert',
$tempAvg,
@@ -2031,14 +2033,17 @@ function fan_spike_analysis(array $history, array $fan, array $system, array $pr
}
$summary = 'No system notice detected in recent samples.';
if ($spiking) {
if ($alertStarted) {
$summary = 'System notice: ' . implode(', ', $reasons);
} elseif ($currentState === 'alert' && $reasons !== []) {
$summary = 'System notice active: ' . implode(', ', $reasons);
}
$alertActive = $currentState === 'alert';
return [
'active' => $spiking,
'active' => $alertActive,
'alert_started' => $alertStarted,
'summary' => $summary,
'rpm_delta' => round($rpmDelta, 1),
'pwm_delta' => round($pwmDelta, 1),
@@ -2046,7 +2051,7 @@ function fan_spike_analysis(array $history, array $fan, array $system, array $pr
'rpm_avg' => round($rpmAvg, 1),
'pwm_avg' => round($pwmAvg, 1),
'temp_avg' => round($tempAvg, 1),
'notice_state' => $spiking ? 'alert' : $currentState,
'notice_state' => $currentState,
'baseline_source' => $currentState === 'alert' ? 'frozen_alert' : 'rolling',
'baseline_samples' => (int)$rollingBaseline['samples'],
];
@@ -2148,8 +2153,9 @@ function add_fan_spike_log(array $spike, array $fan, array $system, array $proce
$rpmDelta = round((float)($spike['rpm_delta'] ?? 0), 1);
$pwmDelta = round((float)($spike['pwm_delta'] ?? 0), 1);
$tempDelta = round((float)($spike['temp_delta'] ?? 0), 1);
$spikeKey = 'fan:' . date('YmdHi');
$loggedProcesses = notice_downward_only($tempDelta, $rpmDelta)
$interval = max(60, (int)setting_value('notice.log_interval_seconds'));
$spikeKey = 'fan:' . (int)floor(time() / $interval);
$loggedProcesses = ((bool)setting_value('notice.downward_only_process_hide') && notice_downward_only($tempDelta, $rpmDelta))
? ['cpu' => [], 'memory' => []]
: $processes;
@@ -2412,7 +2418,7 @@ function send_fan_spike_notify(array $spike, array $fan, array $system, array $p
. number_format((float)($fan['rpm'] ?? 0), 0)
. ' RPM / PWM '
. number_format((float)($fan['pwm'] ?? 0), 0)
. (notice_downward_only($tempDelta, $rpmDelta) ? '' : "\n원인 후보: " . expected_process_detail_text($processes));
. (((bool)setting_value('notice.downward_only_process_hide') && notice_downward_only($tempDelta, $rpmDelta)) ? '' : "\n원인 후보: " . expected_process_detail_text($processes));
return send_ha_notify([
'title' => '시스템 유의사항',
@@ -3224,7 +3230,7 @@ function collect_snapshot(bool $applyFan = true): array
'throttling' => $throttledStatuses['throttling'],
]);
$history = add_battery_remaining_history(sensor_history(240));
$history = add_battery_remaining_history(sensor_history((int)setting_value('battery.history_chart_limit')));
$batteryTrend = battery_trend_history((int)setting_value('battery.trend_hours'));
$batteryProfile = battery_profile_history((int)setting_value('battery.profile_days'));
$battery['remaining'] = battery_remaining_estimate($battery, $history, $batteryTrend, $batteryProfile);
@@ -3251,23 +3257,27 @@ function collect_snapshot(bool $applyFan = true): array
'mem_used_mb' => $mem['used_mb'],
'mem_percent' => $mem['total_mb'] > 0 ? round($mem['used_mb'] / $mem['total_mb'] * 100, 1) : 0,
], $battery['remaining']['seconds'] ?? null);
$history = array_slice($history, -(int)setting_value('battery.history_sync_limit'));
$processes = process_resource_data(6);
$fanSpike = fan_spike_analysis($history, $fan, $system, $processes);
if (!empty($fanSpike['active'])) {
$shouldLogNotice = !empty($fanSpike['alert_started'])
|| (!empty($fanSpike['active']) && (bool)setting_value('notice.log_during_alert'));
if ($shouldLogNotice) {
$latestNoticeNotify = latest_system_notice_notify_epoch();
$spikeLogId = add_fan_spike_log($fanSpike, $fan, $system, $processes);
if ($spikeLogId > 0) {
$fanSpike['log_id'] = $spikeLogId;
if ($latestNoticeNotify <= 0 || time() - $latestNoticeNotify >= HA_NOTIFY_SYSTEM_NOTICE_COOLDOWN) {
$noticeCooldown = (int)setting_value('notify.system_notice_cooldown_seconds');
if ($latestNoticeNotify <= 0 || time() - $latestNoticeNotify >= $noticeCooldown) {
$fanSpike['notify'] = send_fan_spike_notify($fanSpike, $fan, $system, $processes);
} else {
$fanSpike['notify'] = [
'sent' => 0,
'failed' => 0,
'skipped' => 'system_notice_cooldown',
'cooldown_seconds' => HA_NOTIFY_SYSTEM_NOTICE_COOLDOWN,
'cooldown_seconds' => $noticeCooldown,
];
}
}
@@ -3383,6 +3393,14 @@ function control_api_dispatch(): void
$password = (string)($_POST['password'] ?? '');
$expectedPhrase = (string)setting_value('security.reboot_phrase');
if (!(bool)setting_value('security.allow_reboot')) {
json_out([
'ok' => false,
'error' => 'reboot_disabled',
'message' => '재부팅 실행이 설정에서 비활성화되어 있습니다.',
], 403);
}
if ($phrase !== $expectedPhrase) {
json_out([
'ok' => false,
+11 -4
View File
@@ -625,7 +625,7 @@
return `
<span class="settings-toggle">
<input data-setting-key="${key}" type="checkbox" value="1" ${checked ? 'checked' : ''}>
<span>${checked ? 'ON' : 'OFF'}</span>
<span>${checked ? '켜기' : '끄기'}</span>
</span>
`;
}
@@ -662,6 +662,11 @@
if (item.type === 'select' && item.options && item.options[item.default]) {
return item.options[item.default];
}
if (item.type === 'boolean') {
return item.default === true || item.default === 1 || item.default === '1' || item.default === 'true'
? '켜기'
: '끄기';
}
return item.default ?? '';
};
@@ -687,7 +692,7 @@
els.settingsBody.querySelectorAll('.settings-toggle input').forEach(input => {
input.addEventListener('change', () => {
const label = input.closest('.settings-toggle')?.querySelector('span');
if (label) label.textContent = input.checked ? 'ON' : 'OFF';
if (label) label.textContent = input.checked ? '켜기' : '끄기';
});
input.closest('.settings-toggle')?.addEventListener('click', () => {
input.checked = !input.checked;
@@ -1382,8 +1387,10 @@
const m = noticeMetrics(row);
const reasons = [];
if (Math.abs(m.tempDelta) >= 3) reasons.push(t(m.tempDelta >= 0 ? 'tempHigher' : 'tempLower'));
if (Math.abs(m.rpmDelta) >= 1000) reasons.push(t(m.rpmDelta >= 0 ? 'rpmHigher' : 'rpmLower'));
const tempThreshold = Number(settingByKey('notice.temp_delta_threshold', 3));
const rpmThreshold = Number(settingByKey('notice.rpm_delta_threshold', 1000));
if (Math.abs(m.tempDelta) >= tempThreshold) reasons.push(t(m.tempDelta >= 0 ? 'tempHigher' : 'tempLower'));
if (Math.abs(m.rpmDelta) >= rpmThreshold) reasons.push(t(m.rpmDelta >= 0 ? 'rpmHigher' : 'rpmLower'));
return `${t('recordedReason')}: ${reasons.length ? reasons.join(', ') : t('instantChange')}`;
}