From 501f4e1f87f3eeaee8a8f7e16082b9cfc7e402db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20D=C3=A9siles?= <1536672+cdesiles@users.noreply.github.com> Date: Mon, 29 Jun 2026 23:50:48 +0200 Subject: [PATCH] feat(nut): report outage duration and battery % in ntfy ONBATT records the epoch start time and appends current battery charge; ONLINE appends battery charge and the total outage duration (HH:mm:ss) computed from the recorded start, then clears the state. State lives in /run so it self-clears on reboot. Co-Authored-By: Claude Opus 4.8 --- roles/nut/defaults/main.yml | 5 +++++ roles/nut/templates/ups-notify.sh.j2 | 30 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/roles/nut/defaults/main.yml b/roles/nut/defaults/main.yml index e6fdda5..a3d73d6 100644 --- a/roles/nut/defaults/main.yml +++ b/roles/nut/defaults/main.yml @@ -61,3 +61,8 @@ nut_ntfy_server: https://ntfy.jokester.fr # Path of the deployed NOTIFYCMD wrapper. nut_notify_script_path: /usr/local/bin/ups-notify + +# Where ups-notify records the epoch timestamp of the last ONBATT event, so the +# ONLINE notification can report total outage duration. Lives under /run so it +# is cleared on reboot (stale outage state is meaningless after a reboot). +nut_outage_state_file: /run/ups-notify-outage diff --git a/roles/nut/templates/ups-notify.sh.j2 b/roles/nut/templates/ups-notify.sh.j2 index 23c7da6..aca8a14 100644 --- a/roles/nut/templates/ups-notify.sh.j2 +++ b/roles/nut/templates/ups-notify.sh.j2 @@ -14,15 +14,34 @@ NTFY_TOKEN="{{ nut_ntfy_token }}" NTFY_TOKEN="" {% endif %} +UPS_NAME="{{ nut_ups_name }}" +OUTAGE_STATE_FILE="{{ nut_outage_state_file }}" + MESSAGE="${1:-UPS event}" EVENT="${NOTIFYTYPE:-UNKNOWN}" HOST="$(uname -n)" +# Current battery charge (%) as reported by the UPS. Empty if unavailable. +battery_charge() { + upsc "${UPS_NAME}@localhost" battery.charge 2>/dev/null | head -n1 +} + +# Format a duration in whole seconds as HH:mm:ss. +format_duration() { + local total="$1" + printf '%02d:%02d:%02d' $((total / 3600)) $(((total % 3600) / 60)) $((total % 60)) +} + case "$EVENT" in ONBATT) + # Record when line power was lost so ONLINE can report outage duration. + date +%s > "$OUTAGE_STATE_FILE" 2>/dev/null || \ + logger -t ups-notify "Failed to write outage state file $OUTAGE_STATE_FILE" TITLE="UPS on battery — $HOST" PRIORITY="urgent" TAGS="warning,electric_plug" + charge="$(battery_charge)" + [[ -n "$charge" ]] && MESSAGE="$MESSAGE (battery ${charge}%)" ;; LOWBATT) TITLE="UPS low battery — $HOST" @@ -38,6 +57,17 @@ case "$EVENT" in TITLE="UPS back on line power — $HOST" PRIORITY="default" TAGS="white_check_mark,zap" + charge="$(battery_charge)" + [[ -n "$charge" ]] && MESSAGE="$MESSAGE (battery ${charge}%)" + if [[ -r "$OUTAGE_STATE_FILE" ]]; then + start="$(cat "$OUTAGE_STATE_FILE" 2>/dev/null)" + if [[ "$start" =~ ^[0-9]+$ ]]; then + elapsed=$(( $(date +%s) - start )) + (( elapsed < 0 )) && elapsed=0 + MESSAGE="$MESSAGE — outage lasted $(format_duration "$elapsed")" + fi + rm -f "$OUTAGE_STATE_FILE" 2>/dev/null || true + fi ;; COMMBAD|NOCOMM) TITLE="UPS communication lost — $HOST"