From 0b8e8f6892cb6e9a8f8676fe1f1012a80d0fcee7 Mon Sep 17 00:00:00 2001 From: Marius Date: Thu, 27 Aug 2026 14:46:35 +0300 Subject: [PATCH] fix(ups): testul lunar de baterie nu putea da alarma niciodata Verdictul se calcula din CHARGE_DROP, dar la acest UPS battery.charge nu e o masuratoare independenta: driverul nutdrv_qx / Voltronic-QS-Hex o deduce liniar din tensiune intre battery.voltage.low (20.8V) si .high (26.0V). Verificat pe toate probele din 11 luni: 25.76V -> 95.4% -> raportat 95%. Traduse in tensiune, pragurile vechi cereau o cadere de 3.70V pentru FAIR (primul prag care trimite severity warning) si 4.74V pentru POOR, intr-un test de 40 de secunde. Maximul observat in 11 luni a fost 2.59V. De asta raportul a iesit EXCELLENT 11 luni la rand si ar fi facut-o si cu bateria pe moarte. Evaluarea se face acum pe tendinta: mediana ultimelor 3 rulari fata de mediana primelor 6, plus o plasa de siguranta pe tensiune absoluta (<25.0V forteaza FAIR, <24.0V forteaza POOR). Fereastra de 3 impiedica o luna atipica sa declanseze singura alarma - verificat pe feb 2026 (2.59V), care nu bascuelaza verdictul. Istoricul se tine in /var/log/ups-battery-trend.csv, populat cu cele 14 rulari extrase din jurnal (2025-10-06 -> 2026-08-01), ca linia de baza sa fie valida imediat si nu peste 9 luni. Starea la zi: baza 1.82V, recent 2.12V, raport 1.16x -> EXCELLENT. Cresterea de ~16% in 10 luni e reala dar sub banda de avertizare; FAIR s-ar da la 2.73V. Instalat pe pvemini in /opt/scripts/, verificat cap-coada cu testul de baterie anulat, ca sa nu descarce bateria: tendinta calculata corect, template-urile regenerate cu campurile noi, notificarea PVE::Notify trimisa cu succes. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01RRyaDj39hPQ89SZS6URRpS --- .../ups/docs/UPS-BATTERY-REPLACEMENT.md | 26 ++- proxmox/cluster/ups/docs/UPS-MONTHLY-TEST.md | 101 ++++++++-- .../ups/scripts/ups-battery-trend-backfill.sh | 102 ++++++++++ .../cluster/ups/scripts/ups-monthly-test.sh | 189 +++++++++++++++--- 4 files changed, 368 insertions(+), 50 deletions(-) create mode 100644 proxmox/cluster/ups/scripts/ups-battery-trend-backfill.sh diff --git a/proxmox/cluster/ups/docs/UPS-BATTERY-REPLACEMENT.md b/proxmox/cluster/ups/docs/UPS-BATTERY-REPLACEMENT.md index bf42a99..924488a 100644 --- a/proxmox/cluster/ups/docs/UPS-BATTERY-REPLACEMENT.md +++ b/proxmox/cluster/ups/docs/UPS-BATTERY-REPLACEMENT.md @@ -10,12 +10,17 @@ Ghid complet pentru înlocuirea bateriei UPS când cluster-ul Proxmox este compl ### Indicatori din Testele Lunare: +Verdictul lunar se dă pe **tendința căderii de tensiune** față de linia de bază a +bateriei, nu pe praguri absolute de încărcare (vezi +[UPS-MONTHLY-TEST.md](UPS-MONTHLY-TEST.md#evaluare-sănătate-baterie) pentru de ce). + | Indicator | Acțiune | |-----------|---------| -| **Health: POOR** (charge drop > 55%) | ⚠️ URGENT - Înlocuiește IMEDIAT | -| **Health: FAIR** (charge drop 35-55%) | 📅 Planifică în 1-3 luni | -| **Health: GOOD** (charge drop 15-35%) | ✅ Monitorizează lunar | -| **Health: EXCELLENT** (charge drop < 15%) | ✅ Baterie OK | +| **Health: POOR** (≥ 2.0x față de bază, sau minim < 24.0V) | ⚠️ URGENT - Înlocuiește IMEDIAT | +| **Health: FAIR** (1.5-2.0x, sau minim < 25.0V) | 📅 Planifică în 1-3 luni | +| **Health: GOOD** (1.25-1.5x) | ✅ Monitorizează lunar | +| **Health: EXCELLENT** (< 1.25x) | ✅ Baterie OK | +| **Health: BASELINE** | Se colectează istoric, sub 9 rulări | ### Alte Semne: @@ -341,7 +346,18 @@ tail -30 /var/log/ups-monthly-test.log | grep -A 5 "Sănătate baterie" ``` **Așteptat pentru baterie nouă:** -- Charge drop: < 10% (EXCELLENT) +- Raport față de linia de bază: < 1.25x (EXCELLENT) + +**După înlocuire, linia de bază trebuie resetată** — altfel bateria nouă se compară +cu media celei vechi și verdictul iese fals de bun: + +```bash +mv /var/log/ups-battery-trend.csv /var/log/ups-battery-trend.csv.$(date +%Y%m%d).vechi +# primele 9 rulări vor raporta BASELINE, apoi verdictul redevine valid +``` + +Notează data înlocuirii în acest document — jurnalul nu o conține, iar vârsta +bateriei nu se poate deduce din software. - Voltage drop: 1-2V - Recuperare rapidă: < 60 secunde diff --git a/proxmox/cluster/ups/docs/UPS-MONTHLY-TEST.md b/proxmox/cluster/ups/docs/UPS-MONTHLY-TEST.md index de8030a..a80bf64 100644 --- a/proxmox/cluster/ups/docs/UPS-MONTHLY-TEST.md +++ b/proxmox/cluster/ups/docs/UPS-MONTHLY-TEST.md @@ -47,24 +47,90 @@ Script automat pentru testarea lunară a bateriei UPS care rulează pe data de 1 ## Evaluare Sănătate Baterie -Scriptul evaluează sănătatea bateriei bazat pe scăderea încărcării în timpul testului: +**Evaluarea se face pe tendință, nu pe praguri absolute.** Din 2026-08-27; înainte +se folosea scăderea încărcării față de praguri fixe, care nu funcționa — vezi +[De ce s-a schimbat metoda](#de-ce-sa-schimbat-metoda) mai jos. -| Scădere Încărcare | Sănătate | Status | Acțiune Necesară | -|-------------------|----------|--------|------------------| -| < 10% | **EXCELLENT** | ✅ Verde | Nicio acțiune necesară | -| 10-30% | **GOOD** | ✅ Verde | Continuă monitorizarea | -| 30-50% | **FAIR** | ⚠️ Galben | Planifică înlocuire în 3-6 luni | -| > 50% | **POOR** | 🔴 Roșu | **URGENT: Înlocuiește bateria!** | +### Metoda -### Exemple de rezultate reale: +Se măsoară **căderea de tensiune sub sarcină** în cele ~40 de secunde de test și se +compară cu linia de bază a acestei baterii: -**Test 1 (2025-10-06 20:45):** -- Scădere încărcare: 0% (charge reporting delay) -- Scădere tensiune: 1.64V (27.88V → 26.24V) -- Evaluare: **EXCELLENT** -- Recuperare: 30 secunde la 100% +- **linia de bază** = mediana primelor 6 rulări înregistrate, adică bateria în cea + mai bună stare pe care o avem măsurată; +- **valoarea recentă** = mediana ultimelor 3 rulări, ca o lună atipică (frig, + sarcină mare) să nu declanșeze singură o alarmă; +- **raportul** dintre ele urcă pe măsură ce crește rezistența internă a bateriei, + ceea ce dă avertisment cu luni înainte de cedare. -**Notă:** UPS-ul raportează uneori încărcarea cu întârziere. Scăderea tensiunii este un indicator mai precis al capacității bateriei. +| Raport față de bază | Sănătate | Severitate email | Acțiune | +|---------------------|----------|------------------|---------| +| < 1.25x | **EXCELLENT** | info | Nicio acțiune | +| 1.25 – 1.50x | **GOOD** | info | Uzură normală, urmărește lunar | +| 1.50 – 2.00x | **FAIR** | ⚠️ warning | Planifică înlocuirea în 3-6 luni | +| ≥ 2.00x | **POOR** | 🔴 error | **URGENT: înlocuiește bateria** | + +Peste asta există o **plasă de siguranță pe tensiune absolută**, pentru cazul în +care bateria cedează brusc, înainte să apuce mediana să urce: + +| Minim atins în test | Efect | +|---------------------|-------| +| < 25.0 V | forțează cel puțin **FAIR** | +| < 24.0 V | forțează **POOR** direct | + +Până se strâng 9 rulări (6 pentru bază + 3 pentru fereastra recentă), verdictul +este **BASELINE** — se colectează date, severitate `info`. + +### Fișierul de tendință + +`/var/log/ups-battery-trend.csv`, câte o linie per rulare: + +``` +date,voltage_drop,min_voltage,charge_drop,before_load,after_load,duration +2026-08-01,2.12,25.76,5,10,12,40 +``` + +Se poate reconstrui oricând din jurnal cu +`/opt/scripts/ups-battery-trend-backfill.sh --force`. A fost populat inițial cu +14 rulări extrase din `/var/log/ups-monthly-test.log` (2025-10-06 → 2026-08-01), +ca linia de bază să fie validă imediat și nu peste 9 luni. + +### Starea la 2026-08-27 + +| Perioadă | Cădere de tensiune | +|----------|--------------------| +| oct–dec 2025 (linia de bază) | 1.41 – 2.00 V, mediană **1.82 V** | +| iun–aug 2026 (recent) | 2.12 – 2.23 V, mediană **2.12 V** | + +Raport **1.16x** → **EXCELLENT**. Creșterea de ~16% în 10 luni e reală, dar sub +banda de avertizare. Alarma (FAIR) s-ar da la o cădere de ~2.73 V. + +### De ce s-a schimbat metoda + +`battery.charge` **nu e o măsurătoare independentă** la acest UPS. Driverul +`nutdrv_qx` / `Voltronic-QS-Hex` nu are contor de coulombi; deduce încărcarea +liniar din tensiune, între `battery.voltage.low` (20.8 V) și `battery.voltage.high` +(26.0 V): + +``` +charge = (V - 20.8) / (26.0 - 20.8) * 100 +``` + +Verificat pe toate probele din 11 luni: 25.76 V → 95.4% → raportat 95%; +25.29 V → 86.3% → raportat 86%. Se potrivește exact de fiecare dată. + +Pragurile vechi, traduse înapoi în tensiune, cereau: + +| Prag vechi | Însemna cădere de | Observat în 11 luni | +|------------|-------------------|---------------------| +| GOOD (drop 15%) | 2.66 V | maxim 2.59 V | +| FAIR (drop 35%) | **3.70 V** | niciodată | +| POOR (drop 55%) | **4.74 V** | niciodată | + +EXCELLENT și GOOD sunt amândouă severitate `info`, deci primul prag care chiar +trimite un avertisment era FAIR, la o cădere de 3.70 V într-un test de 40 de +secunde. O baterie ajunsă acolo e practic un rezistor. De asta testul a raportat +**EXCELLENT 11 luni la rând** și ar fi făcut-o și cu bateria pe moarte. ## Instalare @@ -396,11 +462,14 @@ Recuperare: 25% în 300 secunde | Indicator | Bun | Acceptabil | Critic | |-----------|-----|------------|--------| | **Vârstă baterie** | < 2 ani | 2-4 ani | > 4 ani | -| **Scădere încărcare** | < 10% | 10-50% | > 50% | -| **Scădere tensiune** | < 2V | 2-5V | > 5V | +| **Raport față de linia de bază** | < 1.25x | 1.25-2.0x | > 2.0x | +| **Tensiune minimă în test** | > 25.0V | 24.0-25.0V | < 24.0V | | **Timp recuperare** | < 1 min | 1-5 min | > 5 min | | **Teste failed** | 0 | 1-2 | > 3 | +Scăderea încărcării nu mai e criteriu — la acest UPS e tensiunea rescalată, nu o +măsurătoare separată. + ### Factori care afectează durata de viață: - **Temperatură:** Ideal 20-25°C (fiecare +10°C reduce durata cu 50%) diff --git a/proxmox/cluster/ups/scripts/ups-battery-trend-backfill.sh b/proxmox/cluster/ups/scripts/ups-battery-trend-backfill.sh new file mode 100644 index 0000000..9630b34 --- /dev/null +++ b/proxmox/cluster/ups/scripts/ups-battery-trend-backfill.sh @@ -0,0 +1,102 @@ +#!/bin/bash +# +# Reconstruiește /var/log/ups-battery-trend.csv din jurnalul testelor lunare. +# +# De ce există: ups-monthly-test.sh evaluează sănătatea bateriei comparând +# căderea de tensiune cu linia de bază a acestei baterii. Fără istoric, linia de +# bază s-ar forma abia după 9 rulări, adică 9 luni. Jurnalul conține însă deja +# măsurătorile, din 2025-10-06 încoace — se extrag de acolo. +# +# Rulare unică. Refuză să suprascrie un CSV existent fără --force, ca să nu +# reseteze din greșeală linia de bază acumulată. +# +# Creat: 2026-08-27 + +set -euo pipefail + +LOGFILE="${LOGFILE:-/var/log/ups-monthly-test.log}" +TRENDFILE="${TRENDFILE:-/var/log/ups-battery-trend.csv}" +FORCE=0 +DRY_RUN=0 + +for arg in "$@"; do + case "$arg" in + --force) FORCE=1 ;; + --dry-run) DRY_RUN=1 ;; + *) echo "Argument necunoscut: $arg" >&2; exit 2 ;; + esac +done + +if [ ! -f "$LOGFILE" ]; then + echo "EROARE: nu găsesc jurnalul $LOGFILE" >&2 + exit 1 +fi + +if [ -f "$TRENDFILE" ] && [ "$FORCE" -eq 0 ] && [ "$DRY_RUN" -eq 0 ]; then + echo "EROARE: $TRENDFILE există deja. Folosește --force ca să-l reconstruiești." >&2 + exit 1 +fi + +# Parserul lucrează doar cu tipare ASCII — etichetele cu diacritice din jurnal +# ("Scădere tensiune") nu se pot potrivi portabil în awk pe toate locale-urile, +# așa că valorile se recalculează din before/min, care apar fără diacritice. +parse_log() { + awk ' + function num(s) { gsub(/[^0-9.]/, "", s); return s } + + /UPS MONTHLY BATTERY TEST - START/ { + active = 1; phase = 0 + bch = ""; bv = ""; bl = ""; mc = ""; mv = ""; al = ""; dur = ""; d = "" + next + } + !active { next } + + /Step 1:/ { phase = 1; next } + /Step 3:/ { phase = 3; d = substr($1, 2); next } + /Step 4:/ { phase = 4; next } + + phase == 1 && /Battery Charge:/ { bch = num($NF); next } + phase == 1 && /Battery Voltage:/ { bv = num($(NF-1)); next } + phase == 1 && /Load:/ { bl = num($NF); next } + + /Minimum Charge:/ { mc = num($NF); next } + /Minimum Voltage:/ { mv = num($(NF-1)); next } + + phase == 4 && /Load:/ { al = num($NF); next } + + / test: [0-9]+ secunde/ { dur = num($(NF-1)); next } + + /UPS MONTHLY BATTERY TEST - COMPLETE/ { + active = 0 + # Blocurile din versiunile vechi de script nu au minimele — se sar. + if (d != "" && mv != "" && bv != "") { + vd = bv - mv + cd = (bch != "" && mc != "") ? bch - mc : "" + printf "%s,%.2f,%s,%s,%s,%s,%s\n", d, vd, mv, cd, bl, al, dur + } + next + } + ' "$LOGFILE" +} + +ROWS=$(parse_log) +COUNT=$(printf '%s\n' "$ROWS" | grep -c ',' || true) + +if [ "$COUNT" -eq 0 ]; then + echo "EROARE: nu s-a extras nicio rulare din $LOGFILE" >&2 + exit 1 +fi + +if [ "$DRY_RUN" -eq 1 ]; then + echo "date,voltage_drop,min_voltage,charge_drop,before_load,after_load,duration" + printf '%s\n' "$ROWS" + echo "--- $COUNT rulari extrase (dry-run, nimic scris) ---" >&2 + exit 0 +fi + +{ + echo "date,voltage_drop,min_voltage,charge_drop,before_load,after_load,duration" + printf '%s\n' "$ROWS" +} > "$TRENDFILE" + +echo "Scris $COUNT rulari in $TRENDFILE" diff --git a/proxmox/cluster/ups/scripts/ups-monthly-test.sh b/proxmox/cluster/ups/scripts/ups-monthly-test.sh index 3335424..78e230f 100644 --- a/proxmox/cluster/ups/scripts/ups-monthly-test.sh +++ b/proxmox/cluster/ups/scripts/ups-monthly-test.sh @@ -12,6 +12,7 @@ # Autor: Claude Code LOGFILE="/var/log/ups-monthly-test.log" +TRENDFILE="/var/log/ups-battery-trend.csv" UPS_NAME="nutdev1" UPS_USER="admin" UPS_PASS="parola99" @@ -25,6 +26,41 @@ log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOGFILE } +# Câte rulări formează linia de bază și câte intră în fereastra recentă. +# Baza = primele rulări înregistrate, adică bateria în starea ei cea mai bună +# pe care o avem măsurată. Fereastra recentă e mediană pe 3, ca o lună +# atipică (frig, sarcină mare) să nu declanșeze singură o alarmă. +BASELINE_RUNS=6 +TREND_WINDOW=3 + +# Adaugă rularea curentă în fișierul de tendință +append_trend() { + if [ ! -f "$TRENDFILE" ]; then + echo "date,voltage_drop,min_voltage,charge_drop,before_load,after_load,duration" > "$TRENDFILE" + fi + echo "$(date '+%Y-%m-%d'),$1,$2,$3,$4,$5,$6" >> "$TRENDFILE" +} + +# Numărul de rulări înregistrate (fără antet) +trend_rows() { + tail -n +2 "$TRENDFILE" 2>/dev/null | grep -c ',' || echo 0 +} + +# Mediana coloanei voltage_drop. $1 = head|tail (cele mai vechi / cele mai noi), +# $2 = câte rânduri. head/tail se aplică pe ordinea cronologică, sortarea abia +# după — altfel s-ar lua cele mai mici valori, nu cele mai vechi. +trend_median() { + local mode="$1" n="$2" + tail -n +2 "$TRENDFILE" 2>/dev/null | cut -d, -f2 | grep -E '^[0-9]+\.?[0-9]*$' \ + | { if [ "$mode" = "head" ]; then head -n "$n"; else tail -n "$n"; fi; } \ + | sort -g \ + | awk '{v[NR]=$1} END { + if (NR == 0) { print "" ; exit } + if (NR % 2) printf "%.2f", v[(NR+1)/2] + else printf "%.2f", (v[NR/2] + v[NR/2+1]) / 2 + }' +} + # Funcție pentru crearea template-urilor de notificare create_templates() { mkdir -p $TEMPLATE_DIR @@ -51,6 +87,9 @@ TEST RESULTS: ------------- Battery Charge Drop: {{ charge_drop }}% Battery Voltage Drop: {{ voltage_drop }}V +Baseline Voltage Drop: {{ baseline_drop }}V ({{ baseline_runs }} rulari de referinta) +Recent Median (3 run): {{ recent_drop }}V +Trend vs Baseline: {{ trend_ratio }}x Minimum Charge Reached: {{ min_charge }}% Minimum Voltage: {{ min_voltage }}V Recovery Time: {{ recovery_time }}s @@ -136,6 +175,14 @@ EOFTEMPLATE
Recovery Time
{{ recovery_time }}s
+
+
Trend vs Baseline
+
{{ trend_ratio }}x
+
+
+
Baseline Drop
+
{{ baseline_drop }}V
+
@@ -183,8 +230,11 @@ EOFTEMPLATE } # Verifică și creează template-urile dacă nu există -if [ ! -f "$TEMPLATE_DIR/ups-battery-test-subject.txt.hbs" ]; then - log "Creating notification templates..." +# Template-urile se rescriu și dacă sunt dintr-o versiune veche, fără câmpurile +# de tendință — altfel raportul ar ieși cu metrici lipsă. +if [ ! -f "$TEMPLATE_DIR/ups-battery-test-subject.txt.hbs" ] || \ + ! grep -q 'trend_ratio' "$TEMPLATE_DIR/ups-battery-test-body.txt.hbs" 2>/dev/null; then + log "Creating/updating notification templates..." create_templates fi @@ -302,41 +352,117 @@ log " Durată test: $TEST_DURATION secunde" log " Scădere încărcare: $CHARGE_DROP% (de la $BEFORE_CHARGE% la $MIN_CHARGE%)" log " Scădere tensiune: $VOLTAGE_DROP V (de la $BEFORE_VOLTAGE V la $MIN_VOLTAGE V)" -# 6. Evaluare sănătate baterie +# 6. Evaluare sănătate baterie — pe tendință, nu pe prag absolut +# +# De ce nu pe prag absolut: driverul nu măsoară încărcarea, o deduce liniar din +# tensiune între battery.voltage.low (20.8V) și .high (26.0V). Un CHARGE_DROP de +# 15% înseamnă deci o cădere sub ~25.2V, iar 55% înseamnă sub ~23.1V — într-un +# test de 40 de secunde. O baterie ajunsă acolo e deja moartă. Pragurile vechi +# nu se puteau atinge, așa că verdictul a ieșit EXCELLENT 11 luni la rând. +# +# Ce se măsoară în schimb: căderea de tensiune sub sarcină, comparată cu linia de +# bază a acestei baterii. Rezistența internă crește pe măsură ce bateria +# îmbătrânește, deci raportul crește lent și dă avertisment din timp. + +append_trend "$VOLTAGE_DROP" "$MIN_VOLTAGE" "$CHARGE_DROP" "$BEFORE_LOAD" "$AFTER_LOAD" "$TEST_DURATION" + +TREND_ROWS=$(trend_rows) +BASELINE_DROP=$(trend_median head $BASELINE_RUNS) +RECENT_DROP=$(trend_median tail $TREND_WINDOW) +BASELINE_RUNS_USED=$BASELINE_RUNS +if [ "$TREND_ROWS" -lt "$BASELINE_RUNS" ]; then + BASELINE_RUNS_USED=$TREND_ROWS +fi + +TREND_RATIO="n/a" +if [ -n "$BASELINE_DROP" ] && [ -n "$RECENT_DROP" ]; then + TREND_RATIO=$(awk -v r="$RECENT_DROP" -v b="$BASELINE_DROP" \ + 'BEGIN { if (b > 0) printf "%.2f", r / b; else print "n/a" }') +fi + +log "" +log "Step 5b: Tendință față de linia de bază..." +log " Rulări înregistrate: $TREND_ROWS (fișier: $TRENDFILE)" +log " Linie de bază (mediana primelor $BASELINE_RUNS_USED): ${BASELINE_DROP:-n/a} V" +log " Recent (mediana ultimelor $TREND_WINDOW): ${RECENT_DROP:-n/a} V" +log " Raport: ${TREND_RATIO}x" + BATTERY_HEALTH="UNKNOWN" HEALTH_CLASS="fair" HEALTH_EMOJI="[INFO]" HEALTH_DESCRIPTION="" RECOMMENDATIONS="" -if [ "$CHARGE_DROP" -lt 15 ]; then - BATTERY_HEALTH="EXCELLENT" - HEALTH_CLASS="excellent" - HEALTH_EMOJI="[OK]" - HEALTH_DESCRIPTION="Battery is in excellent condition with minimal discharge during test." - RECOMMENDATIONS="
  • ✅ Battery is healthy and functioning normally
  • Continue monthly testing
  • No action required
" - log " Sănătate baterie: EXCELENTĂ (scădere < 15%)" -elif [ "$CHARGE_DROP" -lt 35 ]; then - BATTERY_HEALTH="GOOD" +# Praguri de tensiune absolută, ca plasă de siguranță. Nu înlocuiesc tendința — +# prind cazul în care bateria cedează brusc, înainte să apuce media să urce. +FLOOR=$(awk -v v="$MIN_VOLTAGE" 'BEGIN { + if (v == "" ) { print "none" } + else if (v < 24.0) { print "critical" } + else if (v < 25.0) { print "warn" } + else { print "none" } +}') + +if [ "$TREND_ROWS" -lt $((BASELINE_RUNS + TREND_WINDOW)) ] && [ "$FLOOR" = "none" ]; then + BATTERY_HEALTH="BASELINE" HEALTH_CLASS="good" - HEALTH_EMOJI="[OK]" - HEALTH_DESCRIPTION="Battery shows normal wear but performs adequately." - RECOMMENDATIONS="
  • Battery is functioning well
  • Monitor monthly for degradation trends
  • No immediate action needed
" - log " Sănătate baterie: BUNĂ (scădere 15-35%)" -elif [ "$CHARGE_DROP" -lt 55 ]; then - BATTERY_HEALTH="FAIR" - HEALTH_CLASS="fair" - HEALTH_EMOJI="[WARNING]" - HEALTH_DESCRIPTION="Battery shows significant wear and should be monitored closely." - RECOMMENDATIONS="
  • ⚠️ Battery is aging
  • Plan replacement in 3-6 months
  • Increase monitoring frequency
  • Order replacement battery soon
" - log " Sănătate baterie: ACCEPTABILĂ (scădere 35-55%)" -else + HEALTH_EMOJI="[INFO]" + HEALTH_DESCRIPTION="Se colectează linia de bază. Verdictul pe tendință apare după $((BASELINE_RUNS + TREND_WINDOW)) rulări (acum: $TREND_ROWS)." + RECOMMENDATIONS="
  • Istoric insuficient pentru o comparație pe tendință
  • Rulări înregistrate: $TREND_ROWS din $((BASELINE_RUNS + TREND_WINDOW)) necesare
  • Nicio acțiune necesară
" + log " Sănătate baterie: BASELINE (istoric insuficient)" +elif [ "$FLOOR" = "critical" ]; then BATTERY_HEALTH="POOR" HEALTH_CLASS="poor" HEALTH_EMOJI="[CRITICAL]" - HEALTH_DESCRIPTION="Battery is critically weak and requires immediate replacement!" - RECOMMENDATIONS="
  • 🔴 URGENT: Battery needs immediate replacement!
  • Order new battery NOW
  • UPS may not provide adequate protection
  • Risk of unexpected shutdown
" - log " Sănătate baterie: SLABĂ (scădere > 55%) - NECESITĂ ÎNLOCUIRE!" + HEALTH_DESCRIPTION="Tensiunea a coborât sub 24.0V într-un test de $TEST_DURATION secunde. Bateria nu mai susține sarcina." + RECOMMENDATIONS="
  • 🔴 URGENT: înlocuiește bateria acum
  • Minim atins: ${MIN_VOLTAGE}V (cutoff UPS: 20.8V)
  • Autonomia reală e de ordinul secundelor, nu al minutelor
  • Shutdown-ul orchestrat s-ar putea să nu apuce să termine
" + log " Sănătate baterie: SLABĂ (minim ${MIN_VOLTAGE}V < 24.0V) - NECESITĂ ÎNLOCUIRE!" +elif [ "$TREND_RATIO" = "n/a" ]; then + BATTERY_HEALTH="UNKNOWN" + HEALTH_CLASS="fair" + HEALTH_EMOJI="[WARNING]" + HEALTH_DESCRIPTION="Nu s-a putut calcula tendința (fișier de tendință gol sau corupt)." + RECOMMENDATIONS="
  • Verifică $TRENDFILE
  • Verifică dacă upsc întoarce battery.voltage
" + log " Sănătate baterie: NECUNOSCUTĂ (tendință incalculabilă)" +else + RATIO_CLASS=$(awk -v r="$TREND_RATIO" -v f="$FLOOR" 'BEGIN { + c = (r < 1.25) ? 1 : (r < 1.50) ? 2 : (r < 2.00) ? 3 : 4 + if (f == "warn" && c < 3) c = 3 + print c + }') + case "$RATIO_CLASS" in + 1) + BATTERY_HEALTH="EXCELLENT" + HEALTH_CLASS="excellent" + HEALTH_EMOJI="[OK]" + HEALTH_DESCRIPTION="Căderea de tensiune sub sarcină e la nivelul liniei de bază (${TREND_RATIO}x). Bateria nu dă semne de îmbătrânire." + RECOMMENDATIONS="
  • ✅ Bateria e sănătoasă
  • Recent ${RECENT_DROP}V față de baza ${BASELINE_DROP}V
  • Nicio acțiune necesară
" + log " Sănătate baterie: EXCELENTĂ (${TREND_RATIO}x față de bază)" + ;; + 2) + BATTERY_HEALTH="GOOD" + HEALTH_CLASS="good" + HEALTH_EMOJI="[OK]" + HEALTH_DESCRIPTION="Căderea de tensiune a crescut cu 25-50% față de linia de bază (${TREND_RATIO}x). Uzură normală." + RECOMMENDATIONS="
  • Îmbătrânire vizibilă, dar în limite normale
  • Recent ${RECENT_DROP}V față de baza ${BASELINE_DROP}V
  • Urmărește lunar; la 1.5x se planifică înlocuirea
" + log " Sănătate baterie: BUNĂ (${TREND_RATIO}x față de bază)" + ;; + 3) + BATTERY_HEALTH="FAIR" + HEALTH_CLASS="fair" + HEALTH_EMOJI="[WARNING]" + HEALTH_DESCRIPTION="Căderea de tensiune s-a mărit de ${TREND_RATIO}x față de linia de bază. Rezistența internă a crescut semnificativ." + RECOMMENDATIONS="
  • ⚠️ Planifică înlocuirea în 3-6 luni
  • Recent ${RECENT_DROP}V față de baza ${BASELINE_DROP}V
  • Minim atins în test: ${MIN_VOLTAGE}V
  • Comandă bateriile de schimb (2x 12V 7Ah)
" + log " Sănătate baterie: ACCEPTABILĂ (${TREND_RATIO}x față de bază)" + ;; + *) + BATTERY_HEALTH="POOR" + HEALTH_CLASS="poor" + HEALTH_EMOJI="[CRITICAL]" + HEALTH_DESCRIPTION="Căderea de tensiune s-a dublat față de linia de bază (${TREND_RATIO}x). Bateria și-a pierdut capacitatea." + RECOMMENDATIONS="
  • 🔴 URGENT: înlocuiește bateria
  • Recent ${RECENT_DROP}V față de baza ${BASELINE_DROP}V
  • Autonomia nu mai acoperă procedura de shutdown orchestrat
  • Vezi proxmox/cluster/ups/docs/UPS-BATTERY-REPLACEMENT.md
" + log " Sănătate baterie: SLABĂ (${TREND_RATIO}x față de bază) - NECESITĂ ÎNLOCUIRE!" + ;; + esac fi # 7. Monitorizare recuperare (30 secunde) @@ -355,9 +481,9 @@ END_TIME=$(date +%s) RUNTIME=$((END_TIME - START_TIME)) # 9. Determină severity pentru notificare -if [ "$BATTERY_HEALTH" = "EXCELLENT" ] || [ "$BATTERY_HEALTH" = "GOOD" ]; then +if [ "$BATTERY_HEALTH" = "EXCELLENT" ] || [ "$BATTERY_HEALTH" = "GOOD" ] || [ "$BATTERY_HEALTH" = "BASELINE" ]; then SEVERITY="info" -elif [ "$BATTERY_HEALTH" = "FAIR" ]; then +elif [ "$BATTERY_HEALTH" = "FAIR" ] || [ "$BATTERY_HEALTH" = "UNKNOWN" ]; then SEVERITY="warning" else SEVERITY="error" @@ -385,6 +511,10 @@ my \$template_data = { 'health_description' => '$HEALTH_DESCRIPTION', 'charge_drop' => '$CHARGE_DROP', 'voltage_drop' => '$VOLTAGE_DROP', + 'baseline_drop' => '${BASELINE_DROP:-n/a}', + 'recent_drop' => '${RECENT_DROP:-n/a}', + 'trend_ratio' => '$TREND_RATIO', + 'baseline_runs' => '$BASELINE_RUNS_USED', 'min_charge' => '$MIN_CHARGE', 'min_voltage' => '$MIN_VOLTAGE', 'before_charge' => '$BEFORE_CHARGE', @@ -429,6 +559,7 @@ log "UPS MONTHLY BATTERY TEST - COMPLETE" log "Sănătate baterie: $BATTERY_HEALTH" log "Scădere încărcare: $CHARGE_DROP%" log "Scădere tensiune: $VOLTAGE_DROP V" +log "Linie de bază: ${BASELINE_DROP:-n/a} V | recent: ${RECENT_DROP:-n/a} V | raport: ${TREND_RATIO}x" log "Timp total: $RUNTIME secunde" log "========================================"