Ich habe mir neulich eine USV besorgt, die im Fall der Fälle für etwa eine Stunde meinen MacMini, ein Synology NAS, einen Switch und die Fritzbox mit Strom versorgen kann.

Es handelt sich um eine CyberPower 700VA, mit der ich soweit (sie hat bisher nur einen Stecker-zieh-Test gehabt) ganz zufrieden bin.

Zwar war der Anschaffungszweck, dass die USV die wichtigsten Geräte nur ein paar Minuten mit Strom versorgen muss, bis ich den Hausstrom auf Inselbetrieb umgeschaltet habe, allerdings kann es ja auch mal sein, dass ich nicht zu Hause bin oder schlafe.
Für den Fall, dass die Batterie ausgeschöpft ist, hat eine USV die Möglichkeit, ein Gerät herunterzufahren.

Dass die Synology NAS sich mit der USV versteht, war mir schon klar, dass der Mac sich auch mit der USV verbinden lässt, war eine positive Überraschung.

Aber wie kann ich sowohl den Mac wie auch die NAS zusammen herunterfahren lassen?

USV und Synology NAS

Ist die Synology NAS mit der USV per USB verbunden kann man über das DiskStation-Web-Interface unter Systemsteuerung > Hardware & Energie im Reiter USV die USV-Unterstützung aktivieren und dabei auswählen, wann die DiskStation heruntergefahren werden soll – nach soundsovielen Minuten im Batteriebetrieb oder wenn die Batterie denkt, dass jetzt bald Schluss ist.
Dazu kann man die DiskStation auch als Netzwerk-USV-Server einrichten und andere DiskStations mit herunterfahren lassen (mal davon ausgehend, dass jene und der Netzwerkswitch ebenfalls an einer USV hängen).

Nur wie fahre ich in dieser Konstellation meinen Mac herunter?

Nun, ich könnte theoretisch von der Netzwerk-USV-Funktion der DiskStation gebrauch machen. Es gibt zwei Projekte, mit denen man jenes NUT-Protokoll am Mac nutzen könnte, um von der Diskstation die Info über den Batteriebetrieb zu erhalten und den Mac ggf herunterzufahren.
Ich habe mich mit damit allerdings nicht weiter beschäftigt, da ich die USV an den Mac statt an die DiskStation angeschlossen habe.

http://www.apcupsd.org/

https://networkupstools.org/

USV und Mac

Interessanterweise erscheint in den Systemeinstellungen > Energiesparen ein bisher versteckter Reiter USV, wenn ich die USV via USB mit dem Mac verbinde.
Hier lassen sich dann analog zu einem MacBook alternative Ruhezustands-Zeiten definieren und – am wichtigsten – wann der Mac heruntergefahren werden soll, sollte der Batteriebetrieb über einen längeren Zeitraum anhalten und die Ladung zu neige gehen.
 

Mac und DiskStation bei niegrigem Batteriestand herunterfahren

Über die weiter oben erwähnten Network UPS Tools (via MacPorts installierbar) könnte ich auf dem Mac nun einen NUT-Server einrichten, den ich dann auch in den DiskStation-Einstellungen verwenden kann, so dass die DiskStation über die Batterie-Stand informiert ist.

Aber ich dachte mir, das müsste doch auch ohne größere Installation mit einem Skript handlebar sein. Ich muss dafür einfach nur per Kommandozeile den Batteriestatus abgreifen können.
Nach einiger Suche wurde ich beim Befehl pmset fündig.

Mit pmset -g pslog kann man ein Log ausgeben, hier als Beispiel die Ausgabe beschränkt auf die USV-Meldungen und mal zwischendurch gezogenem Strom-Stecker:

$ pmset -g pslog | grep VP700ELCD
 -VP700ELCD (id=1518927872)     100%; charging present: true
 -VP700ELCD (id=1518927872)     100%; discharging; 0:55 remaining present: true
 -VP700ELCD (id=1518927872)     100%; discharging; 0:45 remaining present: true
 -VP700ELCD (id=1518927872)     100%; discharging; 0:57 remaining present: true
 -VP700ELCD (id=1518927872)     99%; discharging; 0:47 remaining present: true
 -VP700ELCD (id=1518927872)     99%; charging present: true
 -VP700ELCD (id=1518927872)     99%; charging present: true

Die Ausgabe muss man mit ctl-C stoppen.

Um die ganze Sache in einem Skript verwenden zu können, ist pmset -g ps | grep VP700ELCD was nur eine Zeile ausgibt, hilfreicher.

Was habe ich nun vor?

Ein Skript prüft all 5 Minuten, ob die USV Strom bekommt (charging present) oder im Batteriemodus läuft (discharging).
Ich lasse mich benachrichtigen, wenn die USV einspringt und bei einem bestimmten Ladestand fahre ich die Synology DiskStation herunter.

Die DiskStation könnte ich in der Theorie per SSH herunterfahren, allerdings habe ich da das Problem, dass man den Shutdown-Befehl als root geben muss.
Dies löse ich über folgenden Umweg: Sollte die DiskStation herunterfahren müssen, erstelle ich vom Mac aus nur eine Datei shutmedown in meinem home-Verzeichnis der Diskstation. Auf der DiskStation läuft all 5 Minuten eine Aufgabe (als root-User), die prüft, ob die Datei vorhanden ist und falls ja, jene löscht und die Diskstation herunterfährt.

Dazu auf der Diskstation eine neue Aufgabe erstellen > Benutzerdefiniertes Skript
 
Benutzer ist root
 
All 5 Minuten sollte ausreichen, da die Batterie eine Stunde durchhält und ich den Ausschaltbefehl am Mac schon bei einem Rest-Batteriestand von 60% gebe.
 
Und hier noch der Pfad zu folgendem Skript:

/volume1/skripte/shutmedown.sh
#!/bin/bash

if [[ -e /volume1/homes/apfelz/triggers/shutmedown ]]; then
  rm /volume1/homes/apfelz/triggers/shutmedown
  shutdown -P now
  fi

Hier die Einfache Version meines Perl-Skriptes auf dem Mac, welches die Datei /volume1/homes/apfelz/triggers/shutmedown auf der DiskStation erstellt. Dazu ist es notwendig, eine ssh-Authentifizierung durch Schlüssel vom Mac zur Synology einzurichten, da man ja nicht vor Ort ist, um das ssh-Kennwort einzutippen.

#!/usr/bin/perl

my ($prozent, $remaining) = undef;
my $status_ok = 0;

my $status = `pmset -g ps | grep VP700ELCD`; # <-- Name der USV ggf abzuändern!

if ($status =~ /(\d+)%/) { $prozent = $1;}
if ($status =~ /charging present/) { $status_ok = 1;}
if ($status =~ /(\d+:\d+) remaining/) { $remaining = $1;}

if ($status_ok) {
  print "Alles OK\n";
} else {
  if ($prozent < 60) {
    # Synology herunterfahren
    # (Username, IP, Pfad ggf. abzuändern!)
    `ssh apfelz@192.168.178.100 "touch /volume1/homes/apfelz/triggers/shutmedown"`
    }
  print "Batteriemodus. $prozent %, $remaining verbleibend\n";
  }

Und jenes Skript lasse ich dann per launchd auf dem Mac alle 5 Minuten laufen.
Der UserName sollte dem Benutzer entsprechen, bei dem die ssh-Authentifizierung an der DiskStation eingerichtet wurde.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>net.apfelz.usvcheck</string>
    <key>UserName</key>
    <string>apfelz</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/perl</string>
        <string>/server/scripts/strom/usv-check.pl</string>
    </array>
    <key>StandardErrorPath</key>
    <string>/Library/Logs/apfelzHome/usvcheck.log</string>
    <key>StandardOutPath</key>
    <string>/Library/Logs/apfelzHome/usvcheck.log</string>
    <key>StartInterval</key>
    <integer>300</integer>
</dict>
</plist>

Hier noch ein umfangreicheres Skript, welches mich zudem per Matrix informiert, dass die USV in Betrieb ist und mich über den Batteriestand all 10% Verlust auf dem Laufenden hält.
Und vor allem nur versucht, die Shutdown-Datei auf der DiskStation zu erstellen, so lange sie noch läuft (sich anpingen lässt).

#!/usr/bin/perl

use File::Slurp;
use mtrx::Client;

my ($prozent, $remaining) = undef;
my $status_ok = 0;
my ($ss,$ii,$hh,$dd,$mm,$yyyy,$wday,$yd,$isdst) = localtime(time);
$yyyy += 1900; $mm++;

my $status = `pmset -g ps | grep VP700ELCD`;

if ($status =~ /(\d+)%/) { $prozent = $1;}
if ($status =~ /charging present/) { $status_ok = 1;}
if ($status =~ /(\d+:\d+) remaining/) { $remaining = $1;}

if ($status_ok) {
  if (-e "/server/scripts/strom/usvstatus") {
    `rm /server/scripts/strom/usvstatus`;
    mtrx::sendMsg("USV: Netzbetrieb wieder hergestellt.");
    print "$dd.$mm.$yyyy Netzbetrieb wieder hergestellt.\n";
    }
} else {
  if (-e "/server/scripts/strom/usvstatus" && $prozent+10 <= read_file("/server/scripts/strom/usvstatus")) {
    mtrx::sendMsg("USV: Es verbleiben $prozent %, $remaining Std. Laufzeit.");
    `echo "$prozent" > /server/scripts/strom/usvstatus`;
  } elsif (!-e "/server/scripts/strom/usvstatus") {
    mtrx::sendMsg("USV: Achtung! Server läuft im Notstrombetrieb.");
    `echo "$prozent" > /server/scripts/strom/usvstatus`;
    print "$dd.$mm.$yyyy Notstrombetrieb festgestellt.\n";
    }
  if ($prozent < 60) {
    # Synology herunterfahren
    my $ping = `ping -c 1 -t 3 -q 192.168.178.100`;
    if ($ping =~ m/ 0.0% packet loss/) {
      `ssh apfelz@192.168.178.100 "touch /volume1/homes/mirco/triggers/shutmedown"`;
      mtrx::sendMsg("USV: Shutdown-Kommando an Synology gesendet");
      print "$dd.$mm.$yyyy Bei $prozent % Batteriestand Shutdown-Kommando an Synology gesendet.\n";
      }
    }
  }