Wie USB-Speicher an der Fritzbox automatisiert monitoren?

Moin

Ich habe an der Fritzbox einen USB Stick, dessen Speicher ich in Intervallen monitoren möchte und bei z.B. 80 % eine Email Warnung bekomme. Falls nicht gesamter Stick möglich, dann 1 FTP Verzeichnis.

Mir fallen diese Ideen ein:

  • Über Homeassi (bevorzugt) (*)
  • Ein Bash Script in der Aufgabenplanung auf der Synology NAS (ja würde gehen = noch Arbeitshypothese)
  • Powershell vom Windows Rechner als Service: Ja ginge aber PC läuft nicht 24/7.
  • Eine andere Idee?

(*) Über das Addon Samba-Backup sichere ich Daten auf den USB Stick an der Fritzbox. Verbindung ist generell möglich. Aber wie mache ich dies in HA? HACS?

Welche Richtung würdet Ihr empfehlen?

Danke

Variante Bash Script über Synology NAS Aufgabenplaner (ja es ginge, aber …)

Gleich vorweg. Ich weiß nicht ob das wirklich eine gute Wahl oder mehr Gefrickel ist aber ich wollte wissen ob es funktioniert.

Folgende Schritte ausführen

  • per SSH by Synology anmelden
  • zum homes Verzeichnis wechseln “volumen1/homes/admin” und dort die Datei .smbcredentials erstellen (keine txt)
username=admin
password=DeinGeheimesKennwort
  • Inhalt von FRITZ.NAS mounten (einmalig bis zum nächsten Neustart, soll aber auch permanent gehen) Das “mnt” Verzeichnis darf nicht schon gemountet sein.
sudo mount -t cifs -o credentials=$HOME/.smbcredentials,vers=3.0,noserverino //19
2.168.xxx.xxx/FRITZ.NAS/ /mnt/
  • Nach erfolgreichem Mounten kann man zu mnt wechseln und mit DIR abfragen
cd /mnt
DIR
total 4
drwxr-xr-x  2 root root    0 Apr  5 17:41 .
drwxr-xr-x 24 root root 4096 Apr  7 19:20 ..
drwxr-xr-x  2 root root    0 Nov 30  2023 Bilder
drwxr-xr-x  2 root root    0 Sep  2  2023 Dokumente
drwxr-xr-x  2 root root    0 Sep 26  2024 FRITZ
drwxr-xr-x  2 root root    0 Sep 19  2023 Musik
drwxr-xr-x  2 root root    0 Apr  5 17:41 USBverzeichnis
drwxr-xr-x  2 root root    0 Nov 30  2023 Videos
  • Im Aufgabenplaner eine Aufgabe unter root anlegen und folgendes Script reinkopieren
Zusammenfassung
#In Synology Aufgabenplaner wird nur bei Fehler eine Mail versendet. Aus diesem Grund produzieren wir exit <> 0
#!/bin/bash

USB_DIR="/mnt/USBverzeichnis"
LIMIT=20 # Kleiner diesem Wert erfolgt eine Mailwarnung

if [ -d "$USB_DIR" ]; then
    # Speicherplatz vom USB Stick holen
    read -r FS SIZE USED_GB AVAIL_GB USED_PERC MOUNT <<< \
        "$(df -h --output=source,size,used,avail,pcent,target "$USB_DIR" | tail -n 1)"

    # Prozentzeichen entfernen
    AVAIL_PERC=$((100 - ${USED_PERC%%%}))

if [ "$AVAIL_PERC" -lt "$LIMIT" ]; then
    {
        echo "Speicherstatus: $AVAIL_PERC% verfuegbar ($AVAIL_GB frei)"
        echo "Groesste Ordner (Top-Level in $USB_DIR):"
        #Listet die 5 größten Ordner absteigend sortiert auf
        du -sh "$USB_DIR"/* 2>/dev/null | sort -hr | head -n 5
    } >&2
    exit 1

    else
        echo "$AVAIL_PERC% verfuegbar ($AVAIL_GB frei)"
        exit 0
    fi
else
    echo "USB-Verzeichnis $USB_DIR nicht gefunden!" >&2
    exit 2
fi

Das Script nutzt den Mount und liest den USB Stick aus. Wenn der verfügbare Speicherplatz kleiner des angegebenen Limits ist, wird durch den EXIT Trick Synolgy dazu gebracht, eine Mail zu versenden. In dieser steht der noch zur Verfügung stehenden Platz und es werden die 5 größten Ordner aufgelistet. Einfluß auf das Format und den Inhalt hat man nicht.

Der Aufgabenplaner hat eine geplante Aufgabe abgeschlossen.

Aufgabe: test
Start: Mon, 07 Apr 2025 21:26:18 +0200
Ende: Mon, 07 Apr 2025 21:26:35 +0200
Aktueller Status: 1 (Unterbrochen)
Standardausgabe/Fehler:
Speicherstatus: 92% verfuegbar (428G frei)
Groesste Ordner (Top-Level in /mnt/USBverzeichnis):
24G /mnt/USBverzeichnis/Musik_Bacardi
9.2G /mnt/USBverzeichnis/Backup
115M /mnt/USBverzeichnis/Kameras
23M /mnt/USBverzeichnis/FRITZ
704K /mnt/USBverzeichnis/Install SanDisk Software.exe

Von diskstation

Ja es geht, und selbst wenn ich gefühlt 100 Versuche brauchte - auf mein 2. Bash Script bin ich stolz aber irgendwie ist es Gefrickel (Auslesen der Ausgabe

    read -r FS SIZE USED_GB AVAIL_GB USED_PERC MOUNT <<< \
        "$(df -h --output=source,size,used,avail,pcent,target "$USB_DIR" | tail -n 1)"

Was ist wenn Synology das ändert bei einem Update. Auch das dieser Exit Trick nötig ist, gefällt mir nicht. Ich muß da nochmal in mich gehen.

Vielleicht habt Ihr weiterbringende Gedanken.

Variante Powershell über Windows Rechner und Aufgabenplanung (besser als Bash Synology Aufgabenplaner)

Vorteile Powershell Variante gegenüber Bash

  • Viel flexibler z.B. Email gestaltbarer oder ein rotierendes klein bleibendes Logfile
  • Kennwort wird verschlüsselt abgelegt

Nachteil:

  • Geht nur wenn PC läuft
  • Initiale Einrichtung ist etwas frickelig (Aufgabenplanung und User Kontext)

Wie eingangs gesagt, eigentlich will ich das mit Homeassi machen weil der läuft ständig, hat Zugriff, nur wie tun? Da fehlen mir die Kenntnisse.

Und hier das komplette Powershell Script inklusive Anleitung.
Viel Spaß beim Ausprobieren.

Zusammenfassung
<#

09. April 2025

Was das Script tut:
-------------------

- Öffnet den Weg zum USB Stick an der Fritzbox über UNC Pfad
- Ermittelt den noch verfügbaren Speicherplatz
- Warnt per Email beim Unterschreiten eines Prozent Limits (zum Beispiel weniger als 30 % nur noch verfügbar) und nennt zusätzlich die 5 größten Ordner und ihre Belegung
- Informiert per Email sofern geprüft aber noch genug Platz vorhanden ist
- Schreibt verschiedene Infos in ein Logfile (zum Debuggen)
- Logfile ist rotierend (löscht ältere Einträge sobald maximal definierte ANzahl von Durchläufen erreicht wurde)


Vorbereitungen:
-------------------

- Einmalig die xml Datei mit Mailzugang angelegt werden (Kennwort wird verschlüsselt). Es ist wichtig, daß dies mit dem gleichem Windows User erzeugt wird, mit dem das Script später laufen soll. (nicht admin)

$credPath = "C:\DeinVerzeichnis\MonitoringFritzboxUSB\FritzNasMailCred.xml"
Get-Credential | Export-Clixml -Path $credPath
Danach erscheint ein Windows Aufforderung zur Eingabe von User/Kennwort und dann wird die XML erzeugt.

- Vom Windows PC müssen sowohl der momentane User als auch der Admin (später für die Aufgabenerstellung) Zugriff auf die Fritz.Nas haben. 
Ich habe einen User mit lesenden Zugriff eingerichtet und den Zugriff als User und Admin unter Windows einmal ausgeführt und gespeichert.

- Ausführungsrechte: Das Script wird als .ps1 Datei abgespeichert und die Ausführung ist aus Sicherheitsgründen bei den meisten Windows Versionen gesperrt. 
Aus guten Gründen aber Du kannst hier den Code selber checken. Nichts Verwerfliches wird getan.

    Option1:
    Zum Testen Powershell Code einfach in die PS Code Editor wie die ISE kopieren ohne zu speichern und dann ausführen

    Option2:
    Man könnte dies durch den Start einer Batch Datei temporär für dieses Script umgehen. 
    Der CMD Code wäre:
        @echo off
        powershell.exe -ExecutionPolicy Bypass -File "C:\DeinVerzeichnis\MonitoringFritzboxUSB\monitoring.ps1"
        pause
    Die Ausgabe erfolgt dann nach Doppelklick im CMD Fenster.

    Option3:
    Über Aufgaben Manager periodisch starten 
    Ich konnte über die GUI des AUfgabenmangers nicht richtig einrichten, auch nicht als Admin geöffnet. Es gab immer wieder User Kontext Probleme.
    Ich nutzte unteren Code per ISE als Admin gestartet

        $UserId = "DEINComputername\DeinUser"  # ← Dein normaler User unter dem der Task ausgeführt werden soll
        $Action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument '-ExecutionPolicy Bypass -File "C:\DeinVerzeichnis\MonitoringFritzboxUSB\monitoring.ps1"'
        $Trigger = New-ScheduledTaskTrigger -Daily -At 11am
        $Principal = New-ScheduledTaskPrincipal -UserId $UserId -LogonType Interactive -RunLevel Highest
        Register-ScheduledTask -TaskName "FritzUSB-Monitor" -Action $Action -Trigger $Trigger -Principal $Principal


Gutes Gelingen!

#>

cls
# === Konfiguration anpassen ===
$basePath     = "C:\MonitoringFritzboxUSB" # Name des Ordners wohin das Logfile geschrieben werden soll
$logPath      = Join-Path $basePath "FritzUSBMonitor.log" # Name des Logfiles
$credPath     = Join-Path $basePath "FritzNasMailCred.xml" # Name der xml Datei die verschlüsselte Credentials enthält - WICHTIG: Der Name muß identisch zu dem sein, wie initial angelegt.

$uncPath      = "\\192.168.xxx.xxx\fritz.nas\DeinUSBverzeichnis" # Name des UNC Pfades zum USB Stick an dert Fritzbox
$totalGB      = 460 # Größe des USB Sticks (ich habe 512 GB aber lt- Fritzbox stehen nur 460 GB zur Verfügung
$limitPercent = 30 # Unter diesem Niveau wird gewarnt
$maxLogRuns   = 5  # Maximal Durchläufe im Log behalten (rotierendes Logfile)

# Mail Server (beim Provider erfragen, hier für GMX)
$smtpServer   = "mail.gmx.de"
$smtpPort     = 587
$fromEmail    = "beispiel@gmx.de" 
$toEmail      = "beispiel@gmx.de"

#Ab hier nichts ändern
$mailSent     = $false
$mailLogLines = @()

# === Log vorbereiten / analysieren ===
$logLines = @()
$runNumber = 1

if (Test-Path $logPath) {
    $logLines = Get-Content $logPath

    if ($logLines.Count -gt 0) {
        # Startzeilen suchen
        $startIndices = @(0..($logLines.Count - 1) | Where-Object { $logLines[$_] -match '^==== Durchlauf \d+' })

        # Nur wenn mehr als erlaubt vorhanden sind, kürzen
        if ($startIndices.Count -gt $maxLogRuns) {
            $startIndexToKeep = $startIndices[-$maxLogRuns]
            $logLines = $logLines[$startIndexToKeep..($logLines.Count - 1)]
            $logLines | Set-Content -Path $logPath -Encoding UTF8
        }

        # Nummer des letzten bekannten Durchlaufs ermitteln
        $logLines = Get-Content $logPath
        $runMarkers = $logLines | Where-Object { $_ -match '^==== Durchlauf \d+' }

        if ($runMarkers.Count -gt 0) {
            $lastNumbers = $runMarkers | ForEach-Object {
                if ($_ -match '^==== Durchlauf (\d+)') { [int]$matches[1] }
            }
            $runNumber = ($lastNumbers | Measure-Object -Maximum).Maximum + 1
        }
    }
}

# === Startzeit ===
$runStart = Get-Date
$runStartString = $runStart.ToString("dd.MM.yyyy HH:mm:ss")
Add-Content -Path $logPath -Value "==== Durchlauf $runNumber – Startzeit: $runStartString ===="

# === Logging starten ===
Start-Transcript -Path $logPath -Append

try {
    if (-not (Test-Path -Path $credPath)) {
        throw "Deine Email Credential-Datei fehlt: $credPath"
    }

    $cred = Import-Clixml -Path $credPath

    if (Test-Path $uncPath) {
        $usedBytes = (Get-ChildItem -Path $uncPath -Recurse -Force -ErrorAction SilentlyContinue |
                      Measure-Object -Property Length -Sum).Sum

        $usedGB      = [math]::Round($usedBytes / 1GB, 2)
        $freeGB      = [math]::Round($totalGB - $usedGB, 2)
        $usedPercent = [math]::Round(($usedGB / $totalGB) * 100, 0)
        $freePercent = [math]::Round(100 - $usedPercent, 0)

        Write-Host "Belegt: $usedGB GB ($usedPercent%)"
        Write-Host "Frei:   $freeGB GB ($freePercent%)"

        $top5Text = ""

        if ($freePercent -lt $limitPercent) {
            # Top 5 Ordner nur bei Warnung analysieren
            $folders = Get-ChildItem -Path $uncPath -Directory -Force -ErrorAction SilentlyContinue
            $folderSizes = foreach ($folder in $folders) {
                $size = (Get-ChildItem -Path $folder.FullName -Recurse -File -Force -ErrorAction SilentlyContinue |
                         Measure-Object -Property Length -Sum).Sum
                [PSCustomObject]@{ Name = $folder.Name; SizeGB = [math]::Round($size / 1GB, 2) }
            }

            $top5 = $folderSizes | Sort-Object -Property SizeGB -Descending | Select-Object -First 5
            $top5Text = "`nTop 5 größte Ordner:`n" + ($top5 | Out-String)

            $subject = "ACHTUNG! FB USB Stick: Mit nur noch $freePercent % wird der Speicher knapp"
        }
        else {
            $subject = "FB USB Stick hat mit $freePercent % noch genug Speicherplatz"
        }

        # Gemeinsamer Body (Aller Text innerhalb @""@ darf nicht aus Formatierungsgünden mit Leerzeichen etc beginnen)
        $body = @"
USB-Statusbericht an der FRITZ!Box:

Gesamt: $totalGB GB
Belegt: $usedGB GB ($usedPercent%)
Frei:   $freeGB GB ($freePercent%)
$top5Text
"@

        # Mail senden
        Send-MailMessage -SmtpServer $smtpServer -Port $smtpPort -UseSsl `
            -Credential $cred -From $fromEmail -To $toEmail `
            -Subject $subject -Body $body `
            -Encoding ([System.Text.Encoding]::UTF8)

        # Maildaten speichern fürs Logfile
        $mailSent = $true
        $sendTime = Get-Date
        $sendTimeString = $sendTime.ToString("dd.MM.yyyy HH:mm:ss")
        $mailLogLines += "==== Mail gesendet am: $sendTimeString ===="
        $mailLogLines += "Betreff: $subject"
        $mailLogLines += "Inhalt:"
        $mailLogLines += $body
        $mailLogLines += ""
    }
    else {
        Write-Error "Dein eingetragener UNC-Pfad ist nicht erreichbar: $uncPath"
    }
}
catch {
    #Ausgabe des Fehlers
    Write-Error "Fehler: $_"
}
finally {
    Stop-Transcript

    if ($mailSent -and $mailLogLines.Count -gt 0) {
        Add-Content -Path $logPath -Value $mailLogLines
    }

    $runEnd = Get-Date
    $runEndString = $runEnd.ToString("dd.MM.yyyy HH:mm:ss")
    Add-Content -Path $logPath -Value "==== Durchlauf $runNumber – Ende: $runEndString ===="
    Add-Content -Path $logPath -Value ""
}

EDIT1:
Ich habe die Doku und Kommentare überarbeitet und Email Betreffszeilen angepaßt.
Heute 11 Uhr bekam ich die erste automatisierte Email :slight_smile:
Aus Testgründen lasse ich die Aufgabe täglich ausführen, werde aber dann nach wöchentlich am Wochende wechseln. Das sollte reichen.

Mir auch und ich bin auch kein Entwickler, :laughing: aber theoretisch könnte so etwas z.B. über die HA Fritzbox Tools Integration ja vermutlich auch umgesetzt werden. Vielleicht einfach mal ein entsprechendes Feature Request dazu starten. :slightly_smiling_face:

Die benötigten Infos sind in dem WebGUI der FB ja vorhanden.


“Nur” das diese Infos nicht in irgendeinem Sensor zur Verfügung gestellt wird.

VG Jim

Kleine Code Korrektur: Mailserver von GMX lautet mail.gmx.net (und nicht .de wie im Code geschrieben - Fehler war mir beim privatisieren des Codes unterlaufen. Hatte das Script heute einem Kollegen gezeigt und dem fiel es sofort auf.)