diff --git a/proxmox/cluster/ups/config/battery-install-date b/proxmox/cluster/ups/config/battery-install-date new file mode 100644 index 0000000..5ebfd83 --- /dev/null +++ b/proxmox/cluster/ups/config/battery-install-date @@ -0,0 +1,15 @@ +# Data instalarii acumulatorilor din UPS, format YYYY-MM-DD, o singura data. +# Se instaleaza pe pvemini in /etc/nut/battery-install-date +# +# Citit de /opt/scripts/ups-monthly-test.sh ca sa raporteze varsta si sa coboare +# verdictul la FAIR peste 4 ani, indiferent de tendinta. Motivul: linia de baza a +# tendintei a fost masurata in oct 2025, cand acumulatorii aveau deja ~21 luni, +# deci raportul nu se compara cu o baterie noua. +# +# LA INLOCUIRE: actualizeaza data AICI si pe server, si reseteaza linia de baza +# (muta /var/log/ups-battery-trend.csv). Vezi UPS-BATTERY-REPLACEMENT.md. +# +# Acumulatori: 2x 12V 7Ah in serie (24V nominal). +# Sursa datei: confirmata de utilizator 2026-08-27, aproximativa ("cred ca"). + +2024-01-09 diff --git a/proxmox/cluster/ups/docs/UPS-BATTERY-REPLACEMENT.md b/proxmox/cluster/ups/docs/UPS-BATTERY-REPLACEMENT.md index 924488a..727a9a6 100644 --- a/proxmox/cluster/ups/docs/UPS-BATTERY-REPLACEMENT.md +++ b/proxmox/cluster/ups/docs/UPS-BATTERY-REPLACEMENT.md @@ -22,6 +22,21 @@ bateriei, nu pe praguri absolute de încărcare (vezi | **Health: EXCELLENT** (< 1.25x) | ✅ Baterie OK | | **Health: BASELINE** | Se colectează istoric, sub 9 rulări | +### Istoricul acestui UPS + +| Ce | Când | Sursă | +|----|------|-------| +| Instalare acumulatori actuali | **2024-01-09** | confirmată de utilizator 2026-08-27, aproximativă | +| Instalare NUT / început monitorizare | 2025-10-06 | jurnal | +| Linia de bază a tendinței | oct–dec 2025 | acumulatori deja de ~21 luni | + +Acumulatori: **2x 12V 7Ah în serie** (24V nominal). Data se ține în +`/etc/nut/battery-install-date`, versionată în +`proxmox/cluster/ups/config/battery-install-date`. + +**Prag de 3 ani: 2027-01-09.** La 4 ani (2028-01-09) testul lunar coboară automat +verdictul la FAIR și începe să trimită avertismente, indiferent de tendință. + ### Alte Semne: - Vârsta bateriei > 3-4 ani @@ -356,8 +371,16 @@ mv /var/log/ups-battery-trend.csv /var/log/ups-battery-trend.csv.$(date +%Y%m%d) # 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. +**Actualizează și data instalării**, în ambele locuri: + +```bash +# pe pvemini +echo "$(date +%Y-%m-%d)" > /etc/nut/battery-install-date +# în repo: proxmox/cluster/ups/config/battery-install-date, plus tabelul de mai sus +``` + +Fără asta, testul lunar continuă să raporteze vârsta bateriei vechi și va coborî +verdictul la FAIR pe criteriu de vârstă deși bateria e nouă. - 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 a80bf64..c42cb5d 100644 --- a/proxmox/cluster/ups/docs/UPS-MONTHLY-TEST.md +++ b/proxmox/cluster/ups/docs/UPS-MONTHLY-TEST.md @@ -95,6 +95,25 @@ Se poate reconstrui oricând din jurnal 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. +### Vârsta acumulatorilor + +Vârsta e al doilea criteriu, independent de tendință, pentru că **linia de bază nu +a fost măsurată pe o baterie nouă** — vezi avertismentul de mai jos. + +Data instalării se ține în `/etc/nut/battery-install-date` (o linie, `YYYY-MM-DD`, +comentariile cu `#` se ignoră). Versionată în +`proxmox/cluster/ups/config/battery-install-date`. + +| Vârstă | Efect asupra verdictului | +|--------|--------------------------| +| < 3 ani | niciunul | +| 3 – 4 ani | notă vizibilă în raport, verdictul rămâne pe tendință | +| > 4 ani | forțează cel puțin **FAIR**, indiferent de tendință | +| necunoscută | notă care cere completarea fișierului | + +Dacă fișierul lipsește sau nu conține o dată validă, scriptul funcționează normal +și raportează vârsta ca necunoscută. + ### Starea la 2026-08-27 | Perioadă | Cădere de tensiune | @@ -105,6 +124,18 @@ ca linia de bază să fie validă imediat și nu peste 9 luni. 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. +Acumulatori instalați **2024-01-09** (dată aproximativă, confirmată de utilizator), +deci **32 de luni** la 2026-08-27. Pragul de 3 ani vine pe 2027-01-09. + +> ⚠️ **Linia de bază nu e o baterie nouă.** NUT a fost instalat pe 2025-10-06, când +> acumulatorii aveau deja ~21 de luni. Raportul de 1.16x măsoară deci degradarea +> *peste* o baterie care își pierduse deja o parte din capacitate — nu față de +> starea din fabrică. Verdictul EXCELLENT e corect ca tendință, dar nu înseamnă +> „baterie ca nouă". De aceea vârsta e criteriu separat. +> +> **La următoarea înlocuire linia de bază se resetează**, și abia atunci raportul +> va porni de la o baterie chiar nouă. + ### De ce s-a schimbat metoda `battery.charge` **nu e o măsurătoare independentă** la acest UPS. Driverul diff --git a/proxmox/cluster/ups/scripts/ups-monthly-test.sh b/proxmox/cluster/ups/scripts/ups-monthly-test.sh index 78e230f..4518a95 100644 --- a/proxmox/cluster/ups/scripts/ups-monthly-test.sh +++ b/proxmox/cluster/ups/scripts/ups-monthly-test.sh @@ -13,6 +13,7 @@ LOGFILE="/var/log/ups-monthly-test.log" TRENDFILE="/var/log/ups-battery-trend.csv" +BATTERY_DATE_FILE="/etc/nut/battery-install-date" UPS_NAME="nutdev1" UPS_USER="admin" UPS_PASS="parola99" @@ -33,6 +34,21 @@ log() { BASELINE_RUNS=6 TREND_WINDOW=3 +# Vârsta acumulatorilor, în luni, din $BATTERY_DATE_FILE (o singură linie, +# format YYYY-MM-DD). Contează separat de tendință: linia de bază se poate să fi +# fost măsurată pe o baterie deja îmbătrânită, caz în care raportul arată bine +# fără ca bateria să fie bună. Vârsta e singurul reper absolut pe care îl avem. +battery_age_months() { + [ -r "$BATTERY_DATE_FILE" ] || return 0 + local d + d=$(grep -v '^[[:space:]]*#' "$BATTERY_DATE_FILE" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}' | head -1) + [ -n "$d" ] || return 0 + local t0 t1 + t0=$(date -d "$d" +%s 2>/dev/null) || return 0 + t1=$(date +%s) + awk -v a="$t0" -v b="$t1" 'BEGIN { printf "%.0f", (b - a) / 2629746 }' +} + # Adaugă rularea curentă în fișierul de tendință append_trend() { if [ ! -f "$TRENDFILE" ]; then @@ -90,6 +106,7 @@ 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 +Battery Age: {{ battery_age }} luni (instalati {{ battery_install }}) Minimum Charge Reached: {{ min_charge }}% Minimum Voltage: {{ min_voltage }}V Recovery Time: {{ recovery_time }}s @@ -183,6 +200,10 @@ EOFTEMPLATE
Baseline Drop
{{ baseline_drop }}V
+
+
Battery Age
+
{{ battery_age }} luni
+
@@ -233,7 +254,7 @@ EOFTEMPLATE # 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 + ! grep -q 'battery_age' "$TEMPLATE_DIR/ups-battery-test-body.txt.hbs" 2>/dev/null; then log "Creating/updating notification templates..." create_templates fi @@ -366,6 +387,21 @@ log " Scădere tensiune: $VOLTAGE_DROP V (de la $BEFORE_VOLTAGE V la $MIN_VOLTA append_trend "$VOLTAGE_DROP" "$MIN_VOLTAGE" "$CHARGE_DROP" "$BEFORE_LOAD" "$AFTER_LOAD" "$TEST_DURATION" +BATTERY_AGE=$(battery_age_months) +BATTERY_INSTALL=$(grep -v '^[[:space:]]*#' "$BATTERY_DATE_FILE" 2>/dev/null | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}' | head -1) + +# 0 = sub 3 ani, 1 = 3-4 ani (doar notă), 2 = peste 4 ani (forțează FAIR). +# VRLA de 7Ah coboară sub 50% capacitate tipic la 3-4 ani, iar tendința nu prinde +# asta dacă linia de bază a fost măsurată pe o baterie deja uzată. +AGE_CLASS=0 +if [ -n "$BATTERY_AGE" ]; then + if [ "$BATTERY_AGE" -ge 48 ]; then + AGE_CLASS=2 + elif [ "$BATTERY_AGE" -ge 36 ]; then + AGE_CLASS=1 + fi +fi + TREND_ROWS=$(trend_rows) BASELINE_DROP=$(trend_median head $BASELINE_RUNS) RECENT_DROP=$(trend_median tail $TREND_WINDOW) @@ -386,6 +422,11 @@ 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" +if [ -n "$BATTERY_AGE" ]; then + log " Vârstă acumulatori: $BATTERY_AGE luni (instalați $BATTERY_INSTALL)" +else + log " Vârstă acumulatori: necunoscută ($BATTERY_DATE_FILE lipsește)" +fi BATTERY_HEALTH="UNKNOWN" HEALTH_CLASS="fair" @@ -393,6 +434,15 @@ HEALTH_EMOJI="[INFO]" HEALTH_DESCRIPTION="" RECOMMENDATIONS="" +AGE_NOTE="" +case "$AGE_CLASS" in + 1) AGE_NOTE="
  • 📅 Acumulatorii au $BATTERY_AGE luni (instalați $BATTERY_INSTALL). VRLA de 7Ah intră în zona de uzură la 3-4 ani — bugetează înlocuirea.
  • " ;; + 2) AGE_NOTE="
  • ⚠️ Acumulatorii au $BATTERY_AGE luni (instalați $BATTERY_INSTALL), peste 4 ani. Verdictul e coborât la FAIR pe criteriu de vârstă, indiferent de tendință.
  • " ;; +esac +if [ -z "$BATTERY_AGE" ]; then + AGE_NOTE="
  • Vârsta acumulatorilor e necunoscută — scrie data instalării în $BATTERY_DATE_FILE (YYYY-MM-DD).
  • " +fi + # 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 { @@ -402,7 +452,7 @@ FLOOR=$(awk -v v="$MIN_VOLTAGE" 'BEGIN { else { print "none" } }') -if [ "$TREND_ROWS" -lt $((BASELINE_RUNS + TREND_WINDOW)) ] && [ "$FLOOR" = "none" ]; then +if [ "$TREND_ROWS" -lt $((BASELINE_RUNS + TREND_WINDOW)) ] && [ "$FLOOR" = "none" ] && [ "$AGE_CLASS" -lt 2 ]; then BATTERY_HEALTH="BASELINE" HEALTH_CLASS="good" HEALTH_EMOJI="[INFO]" @@ -424,9 +474,10 @@ elif [ "$TREND_RATIO" = "n/a" ]; then RECOMMENDATIONS="" log " Sănătate baterie: NECUNOSCUTĂ (tendință incalculabilă)" else - RATIO_CLASS=$(awk -v r="$TREND_RATIO" -v f="$FLOOR" 'BEGIN { + RATIO_CLASS=$(awk -v r="$TREND_RATIO" -v f="$FLOOR" -v a="$AGE_CLASS" 'BEGIN { c = (r < 1.25) ? 1 : (r < 1.50) ? 2 : (r < 2.00) ? 3 : 4 if (f == "warn" && c < 3) c = 3 + if (a == 2 && c < 3) c = 3 print c }') case "$RATIO_CLASS" in @@ -465,6 +516,12 @@ else esac fi +# Nota de vârstă se lipește la finalul listei de recomandări, oricare ar fi +# verdictul — inclusiv pe EXCELLENT, unde altfel s-ar pierde. +if [ -n "$AGE_NOTE" ]; then + RECOMMENDATIONS=$(echo "$RECOMMENDATIONS" | sed "s#\$#${AGE_NOTE}#") +fi + # 7. Monitorizare recuperare (30 secunde) log "" log "Step 6: Monitorizare recuperare baterie..." @@ -515,6 +572,8 @@ my \$template_data = { 'recent_drop' => '${RECENT_DROP:-n/a}', 'trend_ratio' => '$TREND_RATIO', 'baseline_runs' => '$BASELINE_RUNS_USED', + 'battery_age' => '${BATTERY_AGE:-necunoscuta}', + 'battery_install' => '${BATTERY_INSTALL:-necunoscuta}', 'min_charge' => '$MIN_CHARGE', 'min_voltage' => '$MIN_VOLTAGE', 'before_charge' => '$BEFORE_CHARGE', @@ -560,6 +619,7 @@ 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 "Vârstă acumulatori: ${BATTERY_AGE:-necunoscută} luni" log "Timp total: $RUNTIME secunde" log "========================================"