upssched로 UPS 이벤트를 Telegram으로 받기
UPS 상태가 바뀌면 upsmon 이 이벤트를 던진다. NOTIFYCMD 에 스크립트를 걸면 그때마다 실행된다. 그런데 여기에 알림 스크립트를 직접 걸면 곤란한 게 하나 있다.
순간 정전이다. 전기가 1초 깜빡이면 ONBATT와 ONLINE이 연달아 날아오고 알림도 두 번 온다. 하루에 몇 번씩 이러면 알림을 안 보게 된다.
그래서 NOTIFYCMD 에는 upssched 를 걸고, 실제 알림은 upssched 가 조건을 따져서 부르게 했다.
# upsmon.conf
NOTIFYCMD /usr/sbin/upssched
upsmon.conf 의 NOTIFYFLAG 에 EXEC 가 들어 있어야 NOTIFYCMD 가 불린다.
NOTIFYFLAG ONBATT SYSLOG+WALL+EXEC
NOTIFYFLAG ONLINE SYSLOG+WALL+EXEC
NOTIFYFLAG LOWBATT SYSLOG+WALL+EXEC
NOTIFYFLAG FSD SYSLOG+WALL+EXEC
upssched.conf
CMDSCRIPT /etc/nut/ups_notify_telegram
PIPEFN /run/nut/upssched.pipe
LOCKFN /run/nut/upssched.lock
AT ONBATT * START-TIMER onbatt 30
AT ONLINE * CANCEL-TIMER onbatt
AT ONLINE * EXECUTE online
AT LOWBATT * EXECUTE lowbat
AT FSD * EXECUTE fsd
AT SHUTDOWN * EXECUTE powerdown
AT COMMBAD * START-TIMER commbad 15
AT COMMOK * CANCEL-TIMER commbad
AT COMMOK * EXECUTE commok
AT NOCOMM * EXECUTE nocomm
AT NOPARENT * EXECUTE noparent
핵심은 START-TIMER 와 CANCEL-TIMER 쌍이다.
ONBATT가 오면 바로 알리지 않고 30초짜리 타이머를 건다. 그 안에 ONLINE이 오면 타이머를 취소한다. 30초를 넘겨야 진짜 정전으로 보고 알림을 보낸다.
COMMBAD도 같은 방식으로 15초를 준다. USB가 잠깐 끊기는 건 흔하고 대부분 바로 돌아온다.
LOWBATT, FSD, SHUTDOWN은 타이머 없이 즉시 보낸다. 이 시점이면 이미 늦은 상황이라 지연시킬 이유가 없다.
알림 스크립트
CMDSCRIPT 에 지정한 파일은 EXECUTE 의 인자를 $1 로 받는다.
#!/usr/bin/env bash
# NUT CMDSCRIPT
BOT_TOKEN=""
CHAT_ID=""
TIMEOUT=5
HOST="$(hostname)"
NOW="$(date '+%Y-%m-%d %H:%M:%S %Z')"
UPSNAME="${UPSNAME:-unknown}"
NOTIFYTYPE="${NOTIFYTYPE:-unknown}"
NOTIFYMSG="${NOTIFYMSG:-blank}"
TYPE="${1:-unknown}"
case "$TYPE" in
online) TITLE="Power Restored" ;;
onbatt) TITLE="On Battery" ;;
lowbatt) TITLE="Low Battery (Critical)" ;;
fsd) TITLE="Forced Shutdown (FSD)" ;;
commok) TITLE="UPS Communication Restored" ;;
commbad) TITLE="UPS Communication Lost" ;;
powerdown) TITLE="Host Shutting Down" ;;
nocomm) TITLE="No UPS Available" ;;
noparent) TITLE="upsmon Parent Lost" ;;
*) TITLE="UPS Event: ${TYPE}" ;;
esac
TEXT="$TITLE
Host: $HOST
UPS: $UPSNAME
Event: $NOTIFYTYPE
Time: $NOW
Info: $NOTIFYMSG"
curl -sS --max-time "$TIMEOUT" -X POST \
"https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
--data-urlencode "chat_id=${CHAT_ID}" \
--data-urlencode "text=${TEXT}" \
--data-urlencode "disable_web_page_preview=true" \
>/dev/null 2>&1
echo "$(date '+%F %T') Sent: $TYPE ($UPSNAME)" >> /etc/nut/nut-telegram.log
UPSNAME, NOTIFYTYPE, NOTIFYMSG 는 upsmon 이 환경변수로 넘겨준다. 어느 UPS의 어떤 이벤트인지를 여기서 꺼내 쓴다.
--max-time 을 짧게 잡은 이유가 있다. 정전 중이면 인터넷도 같이 나갔을 수 있는데, 알림을 보내려다 스크립트가 오래 붙들려 있으면 종료 과정이 밀린다.
마지막 줄의 로그 파일은 알림이 실제로 나갔는지 나중에 확인하려고 남긴다. curl 출력은 버리고 있어서 이게 없으면 아무 흔적도 안 남는다.