Files
ansible-playbooks/roles/nut/templates/ups-notify.sh.j2
T
Clément Désiles 501f4e1f87 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 <noreply@anthropic.com>
2026-06-29 23:50:48 +02:00

108 lines
3.0 KiB
Django/Jinja

#!/usr/bin/env bash
# Managed by Ansible - DO NOT EDIT MANUALLY
#
# Wrapper invoked by upsmon as NOTIFYCMD.
# upsmon passes the rendered NOTIFYMSG as $1 and sets NOTIFYTYPE in the env.
# See: https://networkupstools.org/docs/man/upsmon.conf.html
set -euo pipefail
NTFY_SERVER="{{ nut_ntfy_server }}"
NTFY_TOPIC="{{ nut_ntfy_topic }}"
{% if nut_ntfy_token is defined %}
NTFY_TOKEN="{{ nut_ntfy_token }}"
{% else %}
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"
PRIORITY="urgent"
TAGS="rotating_light,battery"
;;
FSD|SHUTDOWN)
TITLE="UPS forced shutdown — $HOST"
PRIORITY="max"
TAGS="skull"
;;
ONLINE)
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"
PRIORITY="high"
TAGS="warning,satellite"
;;
COMMOK)
TITLE="UPS communication restored — $HOST"
PRIORITY="default"
TAGS="white_check_mark"
;;
REPLBATT)
TITLE="UPS battery needs replacement — $HOST"
PRIORITY="high"
TAGS="battery,wrench"
;;
*)
TITLE="UPS event ($EVENT) — $HOST"
PRIORITY="default"
TAGS="information_source"
;;
esac
auth_args=()
if [[ -n "$NTFY_TOKEN" ]]; then
auth_args=(-H "Authorization: Bearer $NTFY_TOKEN")
fi
# --max-time is important: upsmon will hang on poweroff if curl blocks.
curl -fsS --max-time 10 \
"${auth_args[@]}" \
-H "Title: $TITLE" \
-H "Priority: $PRIORITY" \
-H "Tags: $TAGS" \
-d "$MESSAGE" \
"${NTFY_SERVER%/}/${NTFY_TOPIC}" >/dev/null || \
logger -t ups-notify "Failed to publish ntfy notification for $EVENT"