[Tutorial] Alle HA Entitäten per CSV auslesen und mit Analyzer Tool oder Excel auswerten

Die Variante, die CSV Datei per Java-Script über die Button-Card zu erzeugen, ist für ad-hoc Auswertungen hervorragend.
Als regelmäßiges Backup ist sie daher nicht optimal, und daher habe ich (mit Hilfe einer KI für die Syntax) einen Weg über Skripte in HA und Shell gewählt.

Die erzeugte Datei ist direkt in Excel einlesbar. und wird mit einem Zeitstempel im Dateinamen versehen.
Falls man den Speicherpfad auf z.b. ein NAS legt, ist sie auch bei einem Ausfall von HA verfügbar.

CSV Export aller Entitäten per Skript

Insgesamt brauchen wir:

  • Ein HA Skript, das alle Entitäten und deren Daten zusammenstellt und in eine Datei schreibt
  • Etwas, was per HA in eine Datei schreiben kann.Dafür habe ich bislang nur eine Datei-Entität mit Einsatzzweck „Benachrichtigung“ gefunden
  • Ein Shell Skript, das im Dateisystem für Ordnung sorgt.
  • Eine Automation, die regelmäßig einen Export anstößt

ich verwende hier <<<Zielverzeichnis für den Export>>>, dies muss für die eigene Verwendung angepasst werden

Skript: Entittäten zusammenstellen

sequence:
  - variables:
      entity_ids_sorted: "{{ states | map(attribute='entity_id') | list | sort }}"
  - data:
      entity_id: notify.entity_dump
      message: >-
        "entity_id";"friendly_name";"device";"domain";"area";"floor";"active";"state"
    action: notify.send_message
  - repeat:
      count: "{{ entity_ids_sorted | count }}"
      sequence:
        - variables:
            id: "{{ entity_ids_sorted[repeat.index-1] }}"
            obj: "{{ states(id) }}"
        - choose:
            - conditions:
                - condition: template
                  value_template: "{{ obj is not none }}"
              sequence:
                - variables:
                    name: "{{ obj.name | default('') }}"
                    dev: "{{ device_id(id) | default('') }}"
                    dev_name: "{{ device_name(id) or dev }}"
                    dom: "{{ id.split('.',1)[0] }}"
                    area: "{{ area_name(id) | default('') }}"
                    area_identifier: "{{ area_id(id) | default('') }}"
                    flo_id: "{{ floor_id(area_identifier) | default('') }}"
                    flo_name: "{{ floor_name(flo_id) | default('') }}"
                    aktiv: >-
                      {{ states(id) not in ['unavailable','unknown','none','']
                      }}
                    state: "{{ states(id) | default('')  }}"
                    state_tr: "{{ state_translated(id) | default('')  }}"
                - data:
                    entity_id: notify.entity_dump
                    message: >-
                      "{{ id }}";"{{ name }}";"{{ dev_name }}";"{{ dom }}";"{{
                      area }}";"{{ flo_name }}";"{{ aktiv }}";"{{ state }}";"{{
                      state_tr }}"
                  action: notify.send_message
  - action: shell_command.manage_entity_dump
    data:
      action: trim
  - action: shell_command.manage_entity_dump
    data:
      action: rename
  - action: shell_command.manage_entity_dump
    data:
      action: purge
alias: Entity Dump to File
description: ""

Datei-Entität für Benachrichtigungen

Integration File hinzufügen

Dort einen Benachrichtigungsdienst auswählen

Den Pfad inkl Dateiname zur export datei eintragen, der Dateiname, der im Shellscript verwendet wird, ist entity_dump.csv - er sollte beibehalten werden.
und es soll kein Zeitstempel geschrieben werden

anschließend die File Entität finden (sieht ähnlich aus wie im Screenshot)

und dann umbenennen wie im Screenshot, dann passt es zum HA Skript

Shell Skript für’s Dateisystem

um das Skript aus HA aufrufen zu können, muss ein shell_command definiert werden.

configuration.yaml:

shell_command: !include shell_command.yaml

shell_command.yaml:

  /config/custom_scripts/entity_dump/manage_entity_dump.sh --action "{{ action }}"

Shell Script

#!/bin/bash

# === Konfiguration ===
DUMP_DIR="<<<Zielverzeichnis für den Export>>>"
BASENAME="entity_dump"
EXTENSION="csv"
DUMP_FILE="${BASENAME}.${EXTENSION}"
DUMP_PATH="${DUMP_DIR}/${DUMP_FILE}"

# === Logging ===
SCRIPT_NAME="$(basename "$0" .sh)"
LOG_FILE="${DUMP_DIR}/${SCRIPT_NAME}.log"

log() {
  echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"
}

# === Hilfe anzeigen ===
show_help() {
  echo "Usage: $SCRIPT_NAME --action [purge|trim|rename] [--dry-run]"
  echo ""
  echo "  --action purge     Löscht alle Dateien mit Präfix außer den 5 neuesten"
  echo "  --action trim      Entfernt die ersten zwei Zeilen aus der Dump-Datei"
  echo "  --action rename    Benennt die Dump-Datei mit Zeitstempel um"
  echo "  --dry-run          Zeigt nur an, was passieren würde"
  echo "  --help             Zeigt diese Hilfe"
}

# === Parameterverarbeitung ===
DRY_RUN=false
while [[ $# -gt 0 ]]; do
  case "$1" in
    --action)
      ACTION="$2"
      shift 2
      ;;
    --dry-run)
      DRY_RUN=true
      shift
      ;;
    --help)
      show_help
      exit 0
      ;;
    *)
      echo "❌ Unbekannter Parameter: $1"
      show_help
      exit 1
      ;;
  esac
done

if [ -z "$ACTION" ]; then
  echo "❌ Fehler: --action fehlt"
  show_help
  exit 1
fi

# === Aktionen ===
case "$ACTION" in
  purge)
    log "🔁 purge – Ältere Dumps löschen (nur 5 neueste behalten)"
    cd "$DUMP_DIR" || { log "❌ Verzeichnis nicht gefunden: $DUMP_DIR"; exit 1; }
    FILES_TO_DELETE=$(ls -t "${BASENAME}"*".${EXTENSION}" 2>/dev/null | tail -n +6)
    if [ -z "$FILES_TO_DELETE" ]; then
      log "ℹ️ Keine Dateien zum Löschen gefunden"
    else
      for file in $FILES_TO_DELETE; do
        if $DRY_RUN; then
          log "🧪 [dry-run] Würde löschen: $file"
        else
          log "🗑️ Lösche: $file"
          rm -f "$file"
        fi
      done
    fi
    ;;

  trim)
    log "✂️ trim – Entferne erste zwei Zeilen aus $DUMP_PATH"
    if [ -f "$DUMP_PATH" ]; then
      if $DRY_RUN; then
        log "🧪 [dry-run] Würde erste zwei Zeilen aus $DUMP_PATH entfernen"
      else
        tail -n +3 "$DUMP_PATH" > "${DUMP_PATH}.tmp" && mv "${DUMP_PATH}.tmp" "$DUMP_PATH"
        log "✅ Trim erfolgreich"
      fi
    else
      log "❌ Datei nicht gefunden: $DUMP_PATH"
      exit 1
    fi
    ;;

  rename)
    TIMESTAMP=$(date -r "$DUMP_PATH" +"%Y%m%d_%H%M%S")
    NEW_NAME="${DUMP_DIR}/${BASENAME}_${TIMESTAMP}.${EXTENSION}"
    log "📦 rename – Benenne $DUMP_PATH → $NEW_NAME"
    if [ -f "$DUMP_PATH" ]; then
      if $DRY_RUN; then
        log "🧪 [dry-run] Würde umbenennen: $DUMP_PATH → $NEW_NAME"
      else
        mv "$DUMP_PATH" "$NEW_NAME"
        log "✅ Umbenennung erfolgreich"
      fi
    else
      log "❌ Datei nicht gefunden: $DUMP_PATH"
      exit 1
    fi
    ;;

  *)
    echo "❌ Unbekannte Aktion: $ACTION"
    show_help
    exit 1
    ;;
esac

Automation

alias: Dump Entities to File
description: ""
triggers:
  - trigger: time
    at: "02:17:00"
conditions: []
actions:
  - action: script.entity_dump_to_file
    metadata: {}
    data: {}
mode: single
4 „Gefällt mir“