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:
martino
2026-09-29 10:45:17 +02:00
parent d14b968930
commit 4c08ef6ea0
8 changed files with 386 additions and 13 deletions
+36 -4
View File
@@ -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)