213 lines
5.6 KiB
Nix
Executable File
213 lines
5.6 KiB
Nix
Executable File
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
namespace,
|
|
...
|
|
}:
|
|
with lib;
|
|
let
|
|
inherit (lib.${namespace}) mkOpt mkBoolOpt;
|
|
cfg = config.${namespace}.power.ups;
|
|
|
|
# Script called by upsmon for every UPS event. Reads NTFY_USER and
|
|
# NTFY_PASSWORD from the environment (injected via EnvironmentFile on the
|
|
# upsmon systemd service). upsmon passes the event type as the first
|
|
# argument (e.g. ONBATT, ONLINE, LOWBATT, FSD, COMMOK, COMMBAD, etc).
|
|
upsNotifyScript = pkgs.writeShellScript "ups-ntfy-notify" ''
|
|
EVENT="$1"
|
|
HOST="$(${pkgs.hostname}/bin/hostname)"
|
|
SERVER="https://ntfy.mjallen.dev"
|
|
TOPIC="ups"
|
|
|
|
case "$EVENT" in
|
|
ONBATT)
|
|
TITLE="UPS on battery: $HOST"
|
|
PRIORITY="high"
|
|
TAGS="battery,rotating_light"
|
|
MESSAGE="Power failure detected. UPS is now running on battery."
|
|
;;
|
|
ONLINE)
|
|
TITLE="UPS back on mains: $HOST"
|
|
PRIORITY="low"
|
|
TAGS="electric_plug,white_check_mark"
|
|
MESSAGE="Power restored. UPS is back on mains power."
|
|
;;
|
|
LOWBATT)
|
|
TITLE="UPS battery LOW: $HOST"
|
|
PRIORITY="urgent"
|
|
TAGS="battery,sos"
|
|
MESSAGE="UPS battery is critically low. Shutdown imminent."
|
|
;;
|
|
FSD)
|
|
TITLE="UPS forced shutdown: $HOST"
|
|
PRIORITY="urgent"
|
|
TAGS="warning,sos"
|
|
MESSAGE="Forced shutdown initiated by UPS."
|
|
;;
|
|
COMMOK)
|
|
TITLE="UPS comms restored: $HOST"
|
|
PRIORITY="low"
|
|
TAGS="electric_plug,white_check_mark"
|
|
MESSAGE="Communication with UPS restored."
|
|
;;
|
|
COMMBAD)
|
|
TITLE="UPS comms lost: $HOST"
|
|
PRIORITY="high"
|
|
TAGS="warning,rotating_light"
|
|
MESSAGE="Lost communication with UPS."
|
|
;;
|
|
SHUTDOWN)
|
|
TITLE="UPS shutdown in progress: $HOST"
|
|
PRIORITY="urgent"
|
|
TAGS="warning,sos"
|
|
MESSAGE="System is shutting down due to UPS condition."
|
|
;;
|
|
REPLBATT)
|
|
TITLE="UPS battery needs replacement: $HOST"
|
|
PRIORITY="default"
|
|
TAGS="battery,warning"
|
|
MESSAGE="UPS reports battery needs replacement."
|
|
;;
|
|
NOCOMM)
|
|
TITLE="UPS unreachable: $HOST"
|
|
PRIORITY="high"
|
|
TAGS="warning,rotating_light"
|
|
MESSAGE="UPS is not reachable."
|
|
;;
|
|
*)
|
|
TITLE="UPS event on $HOST: $EVENT"
|
|
PRIORITY="default"
|
|
TAGS="electric_plug"
|
|
MESSAGE="UPS event: $EVENT"
|
|
;;
|
|
esac
|
|
|
|
${pkgs.curl}/bin/curl -sf \
|
|
--user "$NTFY_USER:$NTFY_PASSWORD" \
|
|
-H "Title: $TITLE" \
|
|
-H "Priority: $PRIORITY" \
|
|
-H "Tags: $TAGS" \
|
|
-d "$MESSAGE" \
|
|
"$SERVER/$TOPIC" || true
|
|
'';
|
|
in
|
|
{
|
|
options.${namespace}.power.ups = {
|
|
enable = mkEnableOption "Enable UPS support";
|
|
|
|
upsName = mkOpt types.str "nas-ups" "Name of the ups";
|
|
upsUser = mkOpt types.str "nas-admin" "Name of the ups user";
|
|
|
|
upsdPort = mkOpt types.int 3493 "Port for upsd";
|
|
|
|
ntfy = {
|
|
enable = mkBoolOpt false "Send ntfy notifications on UPS events";
|
|
envFile = mkOpt types.str "" "Path to env file containing NTFY_USER and NTFY_PASSWORD";
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
assertions = [
|
|
{
|
|
assertion = cfg.upsName != "";
|
|
message = "mjallen.power.ups.upsName must be a non-empty string.";
|
|
}
|
|
{
|
|
assertion = cfg.upsUser != "";
|
|
message = "mjallen.power.ups.upsUser must be a non-empty string.";
|
|
}
|
|
{
|
|
assertion = builtins.hasAttr "jallen-nas/ups_password" config.sops.secrets;
|
|
message = "mjallen.power.ups requires a sops secret \"jallen-nas/ups_password\" to be declared.";
|
|
}
|
|
];
|
|
|
|
power.ups = {
|
|
enable = true;
|
|
openFirewall = true;
|
|
mode = "netserver";
|
|
|
|
ups = {
|
|
"${cfg.upsName}" = {
|
|
description = "NAS UPS";
|
|
driver = "usbhid-ups";
|
|
port = "auto";
|
|
};
|
|
};
|
|
|
|
users."${cfg.upsUser}" = {
|
|
passwordFile = config.sops.secrets."jallen-nas/ups_password".path;
|
|
actions = [ "ALL" ];
|
|
instcmds = [ "ALL" ];
|
|
upsmon = "primary";
|
|
};
|
|
|
|
upsmon = {
|
|
enable = true;
|
|
monitor."${cfg.upsName}" = {
|
|
passwordFile = config.sops.secrets."jallen-nas/ups_password".path;
|
|
user = cfg.upsUser;
|
|
};
|
|
|
|
# Call the notify script for all event types we care about.
|
|
settings = mkIf cfg.ntfy.enable {
|
|
NOTIFYCMD = "${upsNotifyScript}";
|
|
NOTIFYFLAG = [
|
|
[
|
|
"ONLINE"
|
|
"SYSLOG+WALL+EXEC"
|
|
]
|
|
[
|
|
"ONBATT"
|
|
"SYSLOG+WALL+EXEC"
|
|
]
|
|
[
|
|
"LOWBATT"
|
|
"SYSLOG+WALL+EXEC"
|
|
]
|
|
[
|
|
"FSD"
|
|
"SYSLOG+WALL+EXEC"
|
|
]
|
|
[
|
|
"COMMOK"
|
|
"SYSLOG+WALL+EXEC"
|
|
]
|
|
[
|
|
"COMMBAD"
|
|
"SYSLOG+WALL+EXEC"
|
|
]
|
|
[
|
|
"SHUTDOWN"
|
|
"SYSLOG+WALL+EXEC"
|
|
]
|
|
[
|
|
"REPLBATT"
|
|
"SYSLOG+WALL+EXEC"
|
|
]
|
|
[
|
|
"NOCOMM"
|
|
"SYSLOG+WALL+EXEC"
|
|
]
|
|
];
|
|
};
|
|
};
|
|
|
|
upsd = {
|
|
enable = true;
|
|
listen = [
|
|
{
|
|
address = "0.0.0.0";
|
|
port = 3493;
|
|
}
|
|
];
|
|
};
|
|
};
|
|
|
|
# Inject ntfy credentials into the upsmon service so the notify script
|
|
# can read NTFY_USER and NTFY_PASSWORD from the environment.
|
|
systemd.services.upsmon.serviceConfig.EnvironmentFile = mkIf cfg.ntfy.enable [ cfg.ntfy.envFile ];
|
|
};
|
|
}
|