mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-10-09 06:56:37 +00:00
fix(notifications): align active health and temperature payloads
Local source candidate; non-English temperature migration and rendered layout validation remain pending.
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -1503,10 +1503,10 @@ class HealthMonitor:
|
||||
|
||||
def _check_cpu_temperature(self) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Check CPU temperature with temporal logic:
|
||||
Check the maximum reported sensor temperature with temporal logic:
|
||||
- WARNING if temp >80°C sustained for >3 minutes
|
||||
- Auto-clears if temp ≤80°C for 30 seconds
|
||||
- No dismiss button (non-dismissable)
|
||||
- Recorded errors retain their current dismissable flag
|
||||
"""
|
||||
cache_key = 'cpu_temp'
|
||||
current_time = time.time()
|
||||
@@ -1574,15 +1574,19 @@ class HealthMonitor:
|
||||
duration_str = f'{actual_minutes}m {actual_seconds}s' if actual_minutes > 0 else f'{actual_seconds}s'
|
||||
|
||||
status = 'WARNING'
|
||||
reason = f'CPU temperature {max_temp}°C >80°C sustained for {duration_str}'
|
||||
reason = f'Sensor temperature {max_temp}°C >80°C; high samples span {duration_str}'
|
||||
|
||||
# Record non-dismissable error
|
||||
# Record the existing dismissable error contract
|
||||
health_persistence.record_error(
|
||||
error_key='cpu_temperature',
|
||||
category='temperature',
|
||||
severity='WARNING',
|
||||
reason=reason,
|
||||
details={'temperature': max_temp, 'duration': actual_duration, 'dismissable': True}
|
||||
details={
|
||||
'temperature': max_temp, 'duration': actual_duration,
|
||||
'dismissable': True, 'value': max_temp, 'threshold': 80,
|
||||
'details': f'High samples span {duration_str}.',
|
||||
}
|
||||
)
|
||||
elif len(recovery_samples) >= 3:
|
||||
# Temperature has been ≤80°C for 30 seconds - clear the error
|
||||
@@ -1595,7 +1599,7 @@ class HealthMonitor:
|
||||
if health_persistence.is_error_active('cpu_temperature', category='temperature'):
|
||||
# Keep the warning active
|
||||
status = 'WARNING'
|
||||
reason = f'CPU temperature {max_temp}°C still elevated'
|
||||
reason = f'Sensor temperature {max_temp}°C still elevated'
|
||||
else:
|
||||
# No active warning yet
|
||||
status = 'OK'
|
||||
|
||||
@@ -680,7 +680,7 @@ def _format_health_degraded(data: Dict[str, Any],
|
||||
return str(data.get('title') or ''), str(data.get('reason') or '')
|
||||
|
||||
def category_label(item: Dict[str, Any]) -> str:
|
||||
category = str(item.get('key') or '')
|
||||
category = str(item.get('key') or item.get('cat_key') or '')
|
||||
return (
|
||||
runtime_message(f'healthDegraded.categories.{category}', language)
|
||||
or str(item.get('category') or category)
|
||||
@@ -695,7 +695,7 @@ def _format_health_degraded(data: Dict[str, Any],
|
||||
|
||||
def localized_reason(item: Dict[str, Any]) -> str:
|
||||
reason = str(item.get('reason') or '')
|
||||
if str(item.get('key') or '') == 'cpu':
|
||||
if str(item.get('key') or item.get('cat_key') or '') == 'cpu':
|
||||
match = _CPU_SUSTAINED_REASON.fullmatch(reason)
|
||||
if match:
|
||||
return runtime_message(
|
||||
@@ -1081,8 +1081,8 @@ TEMPLATES = {
|
||||
'default_enabled': True,
|
||||
},
|
||||
'temp_high': {
|
||||
'title': '{hostname}: High CPU temperature — {value}°C',
|
||||
'body': 'CPU temperature has reached {value}°C (threshold: {threshold}°C).\n{details}',
|
||||
'title': '{hostname}: High sensor temperature — {value}°C',
|
||||
'body': 'Sensor temperature has reached {value}°C (threshold: {threshold}°C).\n{details}',
|
||||
'label': 'High temperature',
|
||||
'group': 'resources',
|
||||
'default_enabled': True,
|
||||
@@ -1885,6 +1885,38 @@ def render_template(event_type: str, data: Dict[str, Any],
|
||||
}
|
||||
variables.update(data)
|
||||
|
||||
# Old persisted errors and manual events may lack a complete reading.
|
||||
# Accept plain numeric strings, but never interpret booleans or objects as
|
||||
# measurements, nor display an infinite value or an overflowing integer.
|
||||
if event_type == 'temp_high':
|
||||
import math as _math
|
||||
def finite_measurement(key):
|
||||
raw = data.get(key)
|
||||
if type(raw) not in (int, float, str):
|
||||
return False
|
||||
try:
|
||||
return _math.isfinite(float(raw))
|
||||
except (OverflowError, ValueError, TypeError):
|
||||
return False
|
||||
|
||||
if not all(finite_measurement(key) for key in ('value', 'threshold')):
|
||||
template['title'] = runtime_message(
|
||||
'fallback.temperatureAlertTitle', language, hostname=variables['hostname'],
|
||||
)
|
||||
lines = [runtime_message('fallback.temperatureAlertBody', language)]
|
||||
reason = data.get('reason')
|
||||
details = data.get('details')
|
||||
reason = reason.strip() if isinstance(reason, str) else ''
|
||||
details = details.strip() if isinstance(details, str) else ''
|
||||
if reason:
|
||||
lines.append(runtime_message('fallback.recordedReason', language, reason=reason))
|
||||
if details and details != reason:
|
||||
lines.append(runtime_message('fallback.recordedDetails', language, details=details))
|
||||
# Format the localized labels once; do not interpret braces in
|
||||
# recorded raw text as template placeholders a second time.
|
||||
variables['_temperature_fallback_body'] = '\n'.join(lines)
|
||||
template['body'] = '{_temperature_fallback_body}'
|
||||
|
||||
if event_type == 'lxc_update_applied' and isinstance(data.get('lxc_update'), dict):
|
||||
status = str(data['lxc_update'].get('status') or '')
|
||||
localized_status = runtime_message(f'lxcUpdate.status.{status}', language)
|
||||
|
||||
Reference in New Issue
Block a user