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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RRyaDj39hPQ89SZS6URRpS
This commit is contained in:
Marius
2026-08-27 14:46:35 +03:00
parent bf73ccf5b1
commit 0b8e8f6892
4 changed files with 368 additions and 50 deletions

View File

@@ -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

View File

@@ -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%)

View File

@@ -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"

View File

@@ -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
<div class="metric-label">Recovery Time</div>
<div class="metric-value">{{ recovery_time }}s</div>
</div>
<div class="metric">
<div class="metric-label">Trend vs Baseline</div>
<div class="metric-value">{{ trend_ratio }}x</div>
</div>
<div class="metric">
<div class="metric-label">Baseline Drop</div>
<div class="metric-value">{{ baseline_drop }}V</div>
</div>
</div>
<div class="section">
@@ -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="<ul><li>✅ Battery is healthy and functioning normally</li><li>Continue monthly testing</li><li>No action required</li></ul>"
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="<ul><li>Battery is functioning well</li><li>Monitor monthly for degradation trends</li><li>No immediate action needed</li></ul>"
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="<ul><li>⚠️ Battery is aging</li><li>Plan replacement in 3-6 months</li><li>Increase monitoring frequency</li><li>Order replacement battery soon</li></ul>"
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="<ul><li>Istoric insuficient pentru o comparație pe tendință</li><li>Rulări înregistrate: $TREND_ROWS din $((BASELINE_RUNS + TREND_WINDOW)) necesare</li><li>Nicio acțiune necesară</li></ul>"
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="<ul><li>🔴 <strong>URGENT:</strong> Battery needs immediate replacement!</li><li>Order new battery NOW</li><li>UPS may not provide adequate protection</li><li>Risk of unexpected shutdown</li></ul>"
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="<ul><li>🔴 <strong>URGENT:</strong> înlocuiește bateria acum</li><li>Minim atins: ${MIN_VOLTAGE}V (cutoff UPS: 20.8V)</li><li>Autonomia reală e de ordinul secundelor, nu al minutelor</li><li>Shutdown-ul orchestrat s-ar putea să nu apuce să termine</li></ul>"
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="<ul><li>Verifică $TRENDFILE</li><li>Verifică dacă upsc întoarce battery.voltage</li></ul>"
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="<ul><li>✅ Bateria e sănătoasă</li><li>Recent ${RECENT_DROP}V față de baza ${BASELINE_DROP}V</li><li>Nicio acțiune necesară</li></ul>"
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="<ul><li>Îmbătrânire vizibilă, dar în limite normale</li><li>Recent ${RECENT_DROP}V față de baza ${BASELINE_DROP}V</li><li>Urmărește lunar; la 1.5x se planifică înlocuirea</li></ul>"
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="<ul><li>⚠️ Planifică înlocuirea în 3-6 luni</li><li>Recent ${RECENT_DROP}V față de baza ${BASELINE_DROP}V</li><li>Minim atins în test: ${MIN_VOLTAGE}V</li><li>Comandă bateriile de schimb (2x 12V 7Ah)</li></ul>"
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="<ul><li>🔴 <strong>URGENT:</strong> înlocuiește bateria</li><li>Recent ${RECENT_DROP}V față de baza ${BASELINE_DROP}V</li><li>Autonomia nu mai acoperă procedura de shutdown orchestrat</li><li>Vezi proxmox/cluster/ups/docs/UPS-BATTERY-REPLACEMENT.md</li></ul>"
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 "========================================"