Du hast einen Wecker in Home Assistant. Du gehst in den Urlaub. Du schaltest den Wecker manuell aus. Du vergisst, ihn wieder einzuschalten. Oder du schaltest ihn zu früh ein und wirst am letzten Urlaubstag geweckt.

Die Lösung: Der Kalender ist die einzige Quelle der Wahrheit. Wenn dort “Urlaub” steht, schweigt der Wecker. Wenn das Event endet, feuert er wieder. Ohne Schalter, ohne Extra-Automation.

TL;DR

  1. Shell-Script auf dem HA-Server fragt per CalDAV, ob heute ein “urlaub”-Event aktiv ist
  2. command_line Binary Sensor in HA: on = Urlaub, off = kein Urlaub
  3. Wecker-Automations prüfen condition: not auf diesen Sensor — fail-open
  4. Drei Pitfalls, die euch Zeit sparen: CalDAV Config Flow, deprecated YAML-Platform, BusyBox-date

Die Architektur

Kalender-Server (CalDAV)
    |
    v  (curl REPORT, alle 15 Min)
HA: check_urlaub.sh
    |
    v
binary_sensor.urlaub_kalender  [on/off]
    |
    v  (condition: not → state: on)
Set_Weekday_Alarm / Set_Weekend_Alarm

Der Binary Sensor pollt alle 15 Minuten den CalDAV-Server. Für mehrtägige Urlaubs-Events ist das mehr als ausreichend.

Das Script: CalDAV-Abfrage

Das Script läuft direkt im HA-Container und fragt per CalDAV REPORT (RFC 4791) die heutigen Events ab. Wenn ein Event mit “urlaub” im Titel gefunden wird: ON. Sonst: OFF.

#!/bin/bash
# /config/check_urlaub.sh
CALDAV_URL="https://mail.deathstar.lan/dav/calendars/luke.skywalker@rebellion.local/Kalender/"
CALDAV_USER="c3po@rebellion.local"
CALDAV_PASS="<DEIN-CALDAV-PASSWORT>"

START=$(date -u +%Y%m%dT000000Z)
# BusyBox-kompatibel (siehe Pitfall 3)
END=$(date -u -D %s -d $(($(date +%s) + 86400)) +%Y%m%dT000000Z)

RESULT=$(curl -s --max-time 10 -u "$CALDAV_USER:$CALDAV_PASS" \
  -X REPORT -H "Depth: 1" -H "Content-Type: application/xml" \
  "$CALDAV_URL" \
  -d "<?xml version=\"1.0\"?>\
<c:calendar-query xmlns:d=\"DAV:\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\">\
<d:prop><c:calendar-data/></d:prop>\
<c:filter><c:comp-filter name=\"VCALENDAR\">\
<c:comp-filter name=\"VEVENT\">\
<c:time-range start=\"$START\" end=\"$END\"/>\
</c:comp-filter></c:comp-filter></c:filter>\
</c:calendar-query>")

if echo "$RESULT" | grep -qi "urlaub"; then
  echo "ON"
else
  echo "OFF"
fi

Wichtig: Der CalDAV-User braucht Leserechte auf den Kalender des Ziel-Users. Bei Grommunio:

grommunio-admin exmdb luke.skywalker@rebellion.local folder grant 0x9 \
    c3po@rebellion.local foldervisible readany

0x9 ist der Store Root — ohne foldervisible dort sieht SabreDAV die Kalender-Kollektionen im PROPFIND nicht.

HA-Konfiguration

Binary Sensor (configuration.yaml)

command_line:
  - binary_sensor:
      name: "Urlaub Kalender"
      unique_id: urlaub_kalender_caldav
      command: "bash /config/check_urlaub.sh"
      device_class: occupancy
      payload_on: "ON"
      payload_off: "OFF"
      scan_interval: 900

Automation-Condition (automations.yaml)

Die Urlaubs-Prüfung kommt als erste Condition in den bestehenden Wecker:

- id: 'weekday_alarm'
  alias: Set_Weekday_Alarm
  triggers:
  - at: input_datetime.alarm
    trigger: time
  conditions: []
  actions:
  - choose:
    - conditions:
      # Urlaub? → Wecker aus
      - condition: not
        conditions:
          - condition: state
            entity_id: binary_sensor.urlaub_kalender
            state: 'on'
      # Normaler Wochentag?
      - condition: time
        weekday: [mon, tue, wed, thu, fri]
      # Wecker eingeschaltet?
      - condition: state
        entity_id: input_boolean.alarm_on_off
        state: 'on'
      sequence:
        # ... Licht, Musik, Kaffeemaschine

Warum condition: not statt state: 'off'?

Fail-open. Wenn der CalDAV-Server nicht erreichbar ist, wird die Entity unavailable:

Sensor-Statecondition: not → onstate: 'off'
on (Urlaub)Wecker ausWecker aus
off (kein Urlaub)Wecker anWecker an
unavailableWecker anWecker aus

Mit state: 'off' würde ein CalDAV-Ausfall den Wecker dauerhaft stumm schalten. condition: not kehrt die Logik um: nur ein explizites on unterdrückt den Wecker. Alles andere — inklusive Fehler — lässt ihn feuern.

Faustregel: Wenn der Fehlerfall “Wecker klingelt obwohl Urlaub” vs. “Wecker schweigt obwohl Arbeitstag” ist — wähle ersteres.

Die drei Pitfalls

Pitfall 1: CalDAV Config Flow geht nicht für Cross-Account

HA hat eine CalDAV-Integration in der UI (Einstellungen → Integrationen → CalDAV). Die funktioniert — aber nur für den Kalender des authentifizierten Users.

Wenn User A den Kalender von User B lesen soll (z. B. ein Service-Account liest den Chef-Kalender), folgt die Config Flow trotzdem dem Principal von User A. Die Entity zeigt dann einen leeren Kalender.

Kein Fehler, kein Log-Eintrag. Die Entity existiert, hat 0 Events, und man sucht den Fehler woanders.

Workaround: command_line Binary Sensor mit direktem curl-CalDAV-REPORT auf die URL des Ziel-Kalenders. Umgeht die Discovery komplett.

Pitfall 2: YAML CalDAV-Platform ist deprecated (silent fail)

Die alte YAML-Variante:

calendar:
  - platform: caldav
    url: https://...
    username: ...
    password: ...
    custom_calendars:
      - name: "Urlaub"
        calendar: "Kalender"
        search: "(?i)urlaub"

wird in aktuellen HA-Versionen ohne Fehlermeldung ignoriert. config/core/check_config meldet valid. Kein Log-Eintrag. Die Entity wird nie erstellt.

Das ist besonders tückisch, weil die YAML-Platform früher Features hatte, die der Config Flow nicht bietet (custom_calendars mit search-Filter). Man konfiguriert es, startet HA neu, alles sieht OK aus — nur die Entity fehlt.

Pitfall 3: BusyBox-date im HA-Container

Home Assistant OS läuft auf Alpine Linux. Der Container hat BusyBox statt GNU coreutils. Das betrifft date:

# GNU date (funktioniert auf Debian/Ubuntu):
date -d "+1 day" +%Y%m%dT000000Z

# BusyBox date (HA Container):
date: invalid date '+1 day'

BusyBox-kompatible Alternative:

date -u -D %s -d $(($(date +%s) + 86400)) +%Y%m%dT000000Z

Betrifft alle shell_command und command_line Sensoren in HA OS.

Der Workflow

  1. Urlaubs-Event im Kalender eintragen (beliebiger Client — Outlook, Thunderbird, Handy)
  2. Warten bis der nächste 15-Minuten-Poll läuft
  3. binary_sensor.urlaub_kalender wechselt auf on
  4. Wecker-Automations prüfen die Condition und schweigen
  5. Event endet → Sensor geht auf off → Wecker feuert wieder

Kein manueller Eingriff. Eine Quelle der Wahrheit.

Lessons Learned

  • Fail-open schlägt fail-closed bei Weckern. Lieber einmal zu viel geweckt als verschlafen.
  • CalDAV Config Flow in HA ist nicht CalDAV — sie ist eine opinionated Abstraktion. Für Cross-Account-Zugriff braucht man den Rohweg über curl.
  • Silent deprecation ist schlimmer als ein Breaking Change. Fehlermeldungen helfen. Stilles Ignorieren kostet Stunden.
  • Alpine/BusyBox-Kompatibilität ist ein Evergreen-Problem. Jedes Shell-Script, das im HA-Container läuft, braucht einen Test im Container, nicht auf dem Entwickler-Rechner.

Checkliste

  • CalDAV-User hat Leserechte auf den Ziel-Kalender
  • check_urlaub.sh im HA-Container getestet (docker exec homeassistant bash /config/check_urlaub.sh)
  • Binary Sensor in Developer Tools → States sichtbar und mit korrektem State
  • Wecker-Automation nutzt condition: not (fail-open)
  • Kalender-Event angelegt und Sensor reagiert