Inverter-monitor notifications: hardening + tuning (ADR-012) How the NFE inverter-monitor decides what to alert on, and the 2026-08-10 hardening pass that killed the nightly "Battery low" noise and closed several alert-integrity gaps. Source of truth is the code + ADR-012 in nfe-modbus-energy-logger; this is the team-facing summary. What triggered it A "[NFE Nansana] Battery low - below 80 percent" email fired at 79% while the battery was doing normal peak-window arbitrage. 79% is not low; 80% was simply in the alert threshold set, which a normally-cycling battery crosses every evening. An end-to-end review found that plus several real gaps. What changed (all live on the VM) Thresholds 80,50,30,20,10 -> 30,20,10. 80/50 crossed nightly on normal arbitrage. Alerts now fire only at genuinely-low SoC. Override per site with MONITOR_SOC_THRESHOLDS . Debounce is now actually wired. A repeat/oscillating low-SoC crossing is suppressed within a cooldown ( MONITOR_SOC_DEBOUNCE_SECONDS , default 6h). It existed and was tested but had never been connected. Severity scales with the level. A 30% crossing is a WARNING (amber); at or under 10% it is CRITICAL (red). Not every low-SoC is the same red alarm anymore. Silent-outage closed. A dead inverter/link while the Pi stays up used to be invisible (the heartbeat kept firing). New data_stale (critical) / data_restored alerts cover it, after N failed reads ( MONITOR_DATA_STALE_AFTER , default 3), debounced. The old "Inverter offline" is renamed "Monitor offline" - it only ever meant the Pi/host/network is down. No more false outages from one bad frame. A grid/battery source change now needs N consecutive readings to confirm ( MONITOR_GRID_CONFIRM_CYCLES , default 2). A single corrupt Modbus frame can no longer email a false grid-lost/restored pair. Boot-into-low-SoC now alerts (previously silent until it recovered and re-crossed). grid_restored shows the outage duration. Delivery is retried. A one-cycle notifier or SMTP blip no longer silently drops a grid_lost - both the POST and the SMTP send retry. Security. The notifier refuses to bind a non-loopback interface with an empty token, so the /alert endpoint (which emails the whole team) can never be exposed unauthenticated. The alert ladder (unchanged rungs) CRITICAL (red): monitor offline, data stale, SoC at/under 10%, battery empty, fault. WARNING (amber): grid lost (on battery), SoC below a threshold above the floor, over-temp. RESOLVED (green): grid restored, back online, data restored. INFO (blue): daily energy/arbitrage summary. Charge/discharge start-stop do not email individually. Deploy + rollback Built from merged main and deployed to the NFE OpenEMS VM as nfe-monitor:local + nfe-notifier:local (local build; the GHCR pipeline path unlocks once the nfe-monitor package is public). Previous images tagged :prev for rollback ( docker tag nfe-monitor:prev nfe-monitor:local && docker compose up -d ). Verified live: both containers stable (0 restarts), notifier listening + auth enforced (bad token -> 401), monitor heart-beating. Traceable in repo ADR-012-notification-hardening, docs/inverter-monitor.dsl (C4 model), docs/notification-severity-standard.md, PR #69. 255 tests, 99% coverage.