# Interaktiver Reinigungsplan mit Home Assistant

> Ein Reinigungsplan, der sich selbst pflegt: Local Calendar, To-do-Liste und Roborock in Home Assistant zu einem interaktiven, automatischen Putzplan verbunden.

_Quelle: https://tbsch.de/post/2026-05-31-interaktiver-reinigungsplan-mit-home-assistant/ · Stand: 2026-05-31_


<div class="tb-lead">Ein guter Reinigungsplan plant sich selbst - du musst ihn nur einmal vernünftig aufschreiben.</div>


Erinnerst du dich noch an mein Versprechen vom Ende des [Scene-History Beitrags](/post/2024-09-25-aktivierung-von-home-assistant-szenen-verfolgen-part-2/)? Ich mich schon: denn ich hatte angekündigt, dass ich darüber schreibe, wie ich mit *Local calendar*, *To-do list* und *Roborock* einen **interaktiven Reinigungsplan** in *Home Assistant* gebaut habe. Das ist jetzt knapp zwei Jahre her, höchste Zeit also :grin:.

Vorab eine ehrliche Ansage: das hier ist kein One-Click-Setup. Wir bauen mehrere Bausteine zusammen, jeder davon einzeln nicht weltbewegend, in Kombination aber überraschend mächtig. Wer schon Lust hat, kann sich die Reihenfolge gleich merken: **Kalender** als Plan, **Template Sensor** als heutige Wahrheit, **Script** für die eigentliche Reinigung, **Todo-Liste** als Statusanzeige für die Familie und **zwei Automationen** als Klebstoff.

Klingt nach Aufwand? Ist es auch. Klingt nach Spaß? Definitiv :sunglasses:.

Insgesamt sind es **drei Automationen**, die wir bauen: eine, die saugt, eine, die nachts die Status-Liste neu aufbaut, und eine, die nach dem Saugen die heute fälligen Räume in der Liste abhakt. Klingt nach viel, ist aber jede für sich kurz und folgt einer klaren Aufgabe.

## Anforderungen an den Plan

Bevor ich angefangen habe, wollte ich für mich klären, was der Plan eigentlich können soll. Ohne klare Anforderung baust du sonst monatelang am Symptom statt an der Ursache. Mein Anspruch war konkret folgender:

- **Pro Raum eigener Rhythmus.** Wohnzimmer und Flur fast täglich, Schlafzimmer zwei- bis dreimal die Woche, andere Räume nach Bedarf.
- **Wiederholungen pro Tag möglich.** Manche Tage rechtfertigen einen zweiten Durchlauf, andere nicht. Soll der Plan steuern, nicht ich.
- **Nur, wenn niemand stört.** Kein Roborock, wenn das Kind schläft, der Fernseher läuft oder jemand in der betroffenen Etage arbeitet.
- **Selbst-pflegende Status-Anzeige.** Damit meine Frau und ich auf einen Blick sehen, was heute ansteht und was bereits erledigt ist - ganz ohne, dass jemand etwas händisch eintragen muss.

> [!info]
> Mein Setup besteht aus genau **einem** [Roborock S7](https://amzlink.to/az0YfePSum1uy "!sponsored"), der über *Segment Cleaning* gezielt einzelne Räume anfährt. Wenn du mehrere Sauger oder gar mehrere Stockwerke hast, lassen sich die Bausteine erweitern - das Grundprinzip bleibt aber identisch.

## Die Bausteine im Überblick

Bevor ich in die Konfiguration einsteige, kurz die Architektur. Das hilft dir später, dich im Code zurechtzufinden:

| Baustein                                 | Aufgabe                                                                                |
| :--------------------------------------- | :------------------------------------------------------------------------------------- |
| `calendar.auto_vacuum`                   | Hält den Plan: welcher Raum, wie oft, in welchem Zeitfenster.                          |
| `binary_sensor.auto_vacuum_schedule`     | Liest den Kalender und berechnet **heute** und **als Nächstes**.                       |
| `script.turn_on_vacuum_segment_cleaning` | Übersetzt *Home Assistant* Areas in Roborock-Segmente und löst die Reinigung aus.      |
| `todo.auto_vacuum`                       | Status-Anzeige fürs Dashboard mit Fälligkeitsdatum pro Raum.                           |
| `automation.staubsauger_starten`         | Klebstoff: triggert bei passender Gelegenheit, prüft Bedingungen, ruft das Script auf. |
| `automation.staubsauger_todos_bauen`     | Pflegt nachts die Todo-Liste neu, damit Fälligkeiten stimmen.                          |
| `automation.staubsauger_todos_abhaken`   | Hakt die heute fälligen Items nach erfolgreicher Reinigung in der Todo-Liste ab.       |

Es lohnt sich, das einmal vor sich hinzuzeichnen. **Stichwort:** *Skizze schlägt Schreibtisch* :grin:.

## 1. Der Plan im Kalender

Das Herzstück ist ein **Local Calendar** namens `calendar.auto_vacuum`. Anders als bei klassischen Reinigungsplänen ist mein Kalender allerdings nicht **lesbar für Menschen**, sondern **lesbar für Home Assistant**. Drei Event-Typen sind hinterlegt, alle mit eigenen Wiederholungs-Regeln (`RRULE`).

**Bereich-Events** geben an, an welchen Tagen welcher Raum dran ist. Der `summary` folgt strikt dem Schema `area:<area_id>`:

```text
summary: area:wohnzimmer
rrule:   FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR,SA
```

```text
summary: area:schlafzimmer
rrule:   FREQ=WEEKLY;BYDAY=TU,SA
```

**Wiederholungs-Events** geben an, wie oft an einem Tag **zusätzlich** gesaugt werden soll. Ohne Event bleibt es bei einem Durchgang, mit `repeat:once` gibt es einen Extra-Durchgang (also zwei), mit `repeat:twice` gibt es zwei Extras (also drei):

```text
summary: repeat:once
rrule:   FREQ=WEEKLY;BYDAY=MO,FR
```

> [!tip]
> Es lohnt sich, hier mit Bedacht zu entscheiden. Drei Durchgänge bedeuten **dreifache Laufzeit** und **dreifacher Lärm**. Mein Roborock zieht im *max*-Modus außerdem ordentlich an der Batterie. `repeat:once` oder `repeat:twice` nutze ich nur an Tagen, an denen wir verlässlich nicht da sind.

**Clean-Events** definieren Zeitfenster, in denen die Reinigung überhaupt anlaufen darf. Das sind die einzigen Events mit `dateTime` statt `date`, weil hier eine Uhrzeit zählt:

```text
summary: clean
start:   09:00, end: 13:00
rrule:   FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR,SA
```

Auf diese Weise habe ich vormittags und nachmittags je ein Fenster definiert. Innerhalb dieser Fenster sieht der Plan zu, dass die Sache passiert. Außerhalb wird gar nicht erst angefangen.

### 1a. Erläuterungen

Warum drei Event-Typen statt einem? Weil sie **unabhängig voneinander** geändert werden können. Wenn ich morgen entscheide, dass das Schlafzimmer nur noch einmal pro Woche dran ist, ändere ich genau ein `area:`-Event, nichts sonst. Wenn ich eine neue Putzphase mit drei Durchgängen ausprobieren möchte, lege ich ein `repeat:twice`-Event an. Und wenn der Bauarbeiter-Lärm in der Nachbarschaft mich zwingt, ein Zeitfenster zu verschieben, fasse ich nur das `clean`-Event an. **Trennung der Zuständigkeiten**, ganz wie beim Coden :smile:.

## 2. Der Template Sensor: "Was steht heute an?"

Aus den Kalender-Events allein wird noch kein Reinigungsplan. Es braucht eine Stelle, die jeden Moment beantworten kann: *"Welche Räume stehen heute an, wie oft, und wann das nächste Mal?"*. Genau das tut mein **Trigger-basierter Template Sensor** `binary_sensor.auto_vacuum_schedule`.

Bevor wir in den YAML-Schlund steigen, zur Orientierung das **Output-Schema**, das der Sensor liefert. Sein State ist `on`, sobald ein `clean`-Fenster aktiv ist, sonst `off`. Die Attribute sind die interessante Stelle:

```yaml
state: "off"  # oder "on", während eines clean-Fensters
attributes:
  today:
    areas:
      - wohnzimmer
      - flur
    repeat: 1
  next:
    areas:
      schlafzimmer: "2026-05-23"
      flur: "2026-05-22"
      wohnzimmer: "2026-05-22"
    repeat:
      once: "2026-05-22"
```

Und so sieht der Sensor in voller Schönheit aus:

```yaml
- triggers:
    - trigger: homeassistant
      event: start
    - trigger: time_pattern
      hours: /1
  actions:
    - action: calendar.get_events
      data:
        start_date_time: "{{ now().date() }}"
        duration:
          days: 6
      target:
        entity_id: calendar.auto_vacuum
      response_variable: result
    - variables:
        clean_events: >
          {{ result["calendar.auto_vacuum"].events
              | selectattr("summary", "eq", "clean")
              | sort(attribute="start")
              | list }}
        next_areas: >
          {% set areas = result["calendar.auto_vacuum"].events
              | selectattr("summary", "search", "area:")
              | groupby(attribute="summary") %}
          {% set data = namespace(areas=[]) %}
          {% for area, items in areas %}
            {% set data.areas = data.areas + [(
              area | replace("area:", ""),
              items
                | sort(attribute="start")
                | map(attribute="start")
                | first
            )] %}
          {% endfor %}
          {{ dict(data.areas) }}
        next_repeat: >
          {% set repeat = result["calendar.auto_vacuum"].events
              | selectattr("summary", "search", "repeat:")
              | groupby(attribute="summary") %}
          {% set data = namespace(repeat=[]) %}
          {% for rep, items in repeat %}
            {% set data.repeat = data.repeat + [(
              rep | replace("repeat:", ""),
              items
                | sort(attribute="start")
                | map(attribute="start")
                | first
            )] %}
          {% endfor %}
          {{ dict(data.repeat) }}
  binary_sensor:
    - name: auto_vacuum_schedule
      unique_id: auto_vacuum_schedule
      state: >
        {% set clean_ev_start = clean_events
            | map(attribute="start")
            | map("as_datetime")
            | list %}
        {% set clean_ev_end = clean_events
            | map(attribute="end")
            | map("as_datetime")
            | list %}
        {% set clean_ev_map = zip(clean_ev_start, clean_ev_end) | list %}
        {{ clean_ev_map
            | rejectattr("0", "ge", now())
            | rejectattr("1", "lt", now())
            | list
            | count > 0 }}
      attributes:
        next: >
          {{ {
            "areas": next_areas,
            "repeat": next_repeat
          } }}
        today: >
          {% set data = namespace(areas=[], repeat=[]) %}
          {% for area in next_areas.keys() %}
            {% if (next_areas[area] | as_datetime).date() == now().date() %}
              {% set data.areas = data.areas + [area] %}
            {% endif %}
          {% endfor %}
          {% for rep in next_repeat.keys() %}
            {% if (next_repeat[rep] | as_datetime).date() == now().date() %}
              {% set data.repeat = data.repeat + [rep] %}
            {% endif %}
          {% endfor %}
          {{ {
            "areas": data.areas,
            "repeat":
              3 if "twice" in data.repeat
              else 2 if "once" in data.repeat
              else 1
          } }}
```

### 2a. Erläuterungen

Sieht nach viel aus, ist im Grunde aber überschaubar. Lass uns das einmal von oben nach unten durchgehen:

1. **Triggers.** Der Sensor wird beim *Home Assistant* Start und einmal pro Stunde neu berechnet. Häufiger ist nicht nötig, weil sich der Kalender ohnehin nicht im Minutentakt ändert.
2. **`calendar.get_events`.** Holt alle Events der nächsten sechs Tage und legt sie als `result` ab. Das ist die einzige externe Datenquelle, alles andere ist Rechenarbeit auf dem Ergebnis.
3. **Variable `clean_events`.** Filtert auf die `summary: clean`-Einträge. Aus dieser Liste wird gleich der State berechnet.
4. **Variable `next_areas`.** Gruppiert alle `area:*`-Events nach Bereich und schnappt sich pro Bereich den **frühesten** kommenden Termin. Ergibt das Dict im `next.areas`-Attribut.
5. **Variable `next_repeat`.** Macht dasselbe für `repeat:*`-Events und liefert pro Wiederholungs-Typ (`once`, `twice`) den nächsten Termin.
6. **State.** Vergleicht `now()` gegen die Start- und End-Zeiten der `clean`-Events. Sobald **genau ein** Fenster aktuell läuft, ist der Sensor `on`.
7. **Attribut `next`.** Einfach die beiden Dicts gebündelt - das ist die **Status-Ebene** für die Todo-Liste.
8. **Attribut `today`.** Filtert beide Dicts auf das heutige Datum. Aus den Wiederholungs-Treffern entsteht der numerische `repeat`-Wert: drei bei `twice`, zwei bei `once`, sonst eins. Das ist die **Aktions-Ebene** für die Automation.

> [!info]
> Beachte die Reihenfolge in der `repeat`-Logik: `twice` wird **vor** `once` geprüft. Wäre es umgekehrt, würde ein Tag, an dem beide Events liegen, fälschlich nur zwei Durchgänge bekommen. So gewinnt immer der höhere Wert.

Das schöne Detail an diesem Sensor ist, dass er **komplett ohne Helfer** auskommt. Keine `input_*`-Entities, keine externen Trigger-Quellen, kein Zustand außer dem Kalender. Wenn morgen mein Setup zerschießt, reichen Kalender plus Template-Sensor, um das System wieder ans Laufen zu bringen.

## 3. Das Script: Areas zu Roborock-Segmenten übersetzen

*Home Assistant* kennt Räume als `area_id`. Mein Roborock kennt Räume als **numerische Segment-IDs**, die er sich beim Mapping selbst vergibt. Das passt nicht zusammen. Also baue ich die Brücke in einem Skript namens `script.turn_on_vacuum_segment_cleaning`:

```yaml
alias: Segmentreinigung starten
icon: mdi:robot-vacuum
fields:
  area:
    name: Raum
    description: Name des Raums
    required: true
    selector:
      area:
        multiple: true
  repeat:
    name: Wiederholungen
    description: Wie oft die Segmente gesaugt werden sollen
    required: false
    default: 1
    selector:
      number:
        min: 1
        max: 3
        step: 1
sequence:
  - action: roborock.get_maps
    data: {}
    target:
      entity_id: vacuum.roborock_s7
    response_variable: result
  - variables:
      segments: >
        {% set rooms = result["vacuum.roborock_s7"].maps
          | selectattr("name", "eq", "Obergeschoss")
          | map(attribute="rooms")
          | first %}
        {% set room_map = zip(
            rooms.values() | map("area_id"),
            rooms.keys()
          ) | list %}
        {{ room_map | selectattr("0", "in", area) | map(attribute="1") | list }}
  - action: vacuum.send_command
    data:
      command: app_segment_clean
      params:
        - segments: "{{ segments }}"
          repeat: "{{ repeat }}"
    target:
      entity_id: vacuum.roborock_s7
```

### 3a. Erläuterungen

Drei Schritte, drei Ideen:

1. Das Script fragt die [Roborock Integration](https://www.home-assistant.io/integrations/roborock/) per `roborock.get_maps` nach den **bekannten Karten** und legt die Antwort in `result` ab. Bei einer Karte pro Etage filtere ich gezielt die Karte namens *Obergeschoss* heraus.
2. Aus den Räumen der Karte baue ich eine **Mapping-Liste** der Form `(area_id, segment_id)`. Das ist das eigentliche Bindeglied zwischen Home-Assistant-Welt und Roborock-Welt.
3. Anschließend filtere ich auf jene `area_id`s, die als Argument übergeben wurden, und feuere das **proprietäre `app_segment_clean`-Kommando** an den Sauger ab. Inklusive der gewünschten Wiederholungs-Anzahl.

> [!warning]
> Damit dieses Mapping funktioniert, müssen die **Raumnamen im Roborock** mit den **Area-IDs in Home Assistant** korrespondieren. Bei mir heißt der Roborock-Raum *Wohnzimmer*, die Area in HA `wohnzimmer`. Roborock leitet daraus dieselbe `area_id` ab. Wenn deine Namen abweichen, musst du die Map einmal manuell pflegen oder via Lookup-Tabelle ergänzen.

## 4. Die Trigger-Automation: "Saug, wenn's niemanden stört"

Jetzt kommt der Klebstoff. Diese Automation entscheidet, **wann** der Sauger tatsächlich loslaufen darf:

```yaml
alias: "Staubsauger: starten"
mode: single
trigger:
  - platform: state
    entity_id: input_select.presence_mode
    from: zuhause
    to: abwesend
    for: "00:15:00"
  - platform: state
    entity_id: person.partner
    to: not_home
    for: "00:30:00"
  - platform: time_pattern
    minutes: "/15"
    seconds: 40
  - platform: calendar
    entity_id: calendar.auto_vacuum
    event: start
condition:
  - condition: state
    entity_id: binary_sensor.auto_vacuum_schedule
    state: "on"
  - condition: state
    entity_id:
      - input_boolean.auto_vacuumed_today
      - input_boolean.vacation_mode
    state: "off"
  - condition: state
    entity_id: input_select.sleep_mode
    state: aus
  - condition: or
    conditions:
      - condition: state
        entity_id: input_select.presence_mode
        state: abwesend
        for: "00:15:00"
      - condition: not
        conditions:
          - condition: state
            entity_id: person.partner
            state: home
  - condition: state
    entity_id: media_player.wohnzimmer_tv
    state: "off"
    for: "00:10:00"
action:
  - action: script.turn_on_vacuum_segment_cleaning
    data:
      area: "{{ state_attr('binary_sensor.auto_vacuum_schedule', 'today').areas or [] }}"
      repeat: "{{ state_attr('binary_sensor.auto_vacuum_schedule', 'today').repeat or 1 }}"
  - wait_for_trigger:
      - platform: state
        entity_id: binary_sensor.roborock_s7_reinigen
        from: "off"
        to: "on"
    continue_on_timeout: true
    timeout: "00:05:00"
  - if:
      - condition: template
        value_template: "{{ not wait.trigger }}"
    then:
      - action: notify.mobile_app
        data:
          message: "Staubsauger sollte gestartet werden, reinigt aber nicht."
      - stop: "Der Staubsauger ist nicht losgefahren."
        error: true
  - action: input_boolean.turn_on
    target:
      entity_id: input_boolean.auto_vacuumed_today
  - action: vacuum.set_fan_speed
    target:
      entity_id: vacuum.roborock_s7
    data:
      fan_speed: max
  - action: select.select_option
    target:
      entity_id: select.roborock_s7_wisch_intensitat
    data:
      option: "off"
```

### 4a. Erläuterungen

Vier Trigger, eine Wahrheit:

1. **Presence-Mode wechselt auf abwesend** für mindestens 15 Minuten. Wenn die ganze Familie das Haus verlässt, beginnt das Fenster.
2. **Partner geht außer Haus`** für 30 Minuten. Mein Büro ist auf einem anderen Stockwerk, also kann gesaugt werden, wenn meine Frau nicht da ist.
3. **Alle 15 Minuten ein Tick**, weil sich Zustände auch zwischendurch ändern können, ohne dass ein State-Trigger feuert.
4. **Kalender-Start eines `clean`-Events**. Wenn das Zeitfenster aufgeht und gerade niemand da ist, soll der Sauger loslegen.

Die Conditions sind das eigentliche Hirn. Sie stellen sicher, dass der Sauger **wirklich** loslaufen darf:

- `binary_sensor.auto_vacuum_schedule` muss `on` sein, sprich: heute steht etwas an und das Zeitfenster läuft.
- Weder *Urlaubsmodus* noch *heute schon gesaugt* dürfen aktiv sein.
- Kein **Schlaf-Modus**, kein laufender **Fernseher** seit mindestens 10 Minuten.
- Die Anwesenheits-Bedingung wird im `or`-Block doppelt geprüft, damit das System bei einer einzelnen Person im Haus auch reagiert, sobald sie unterwegs ist.

Im `action`-Block wird das Script gerufen, dann **fünf Minuten lang** auf das Live-Signal *"Sauger reinigt"* gewartet. Springt er nicht an, gibt es eine Push-Notification - das hat mich am Anfang vor mehr als einem stillen Fehlversuch bewahrt :smile:. Erfolg setzt das *gesaugt-heute*-Flag, schaltet den Lüfter auf maximale Saugstufe und deaktiviert die Wischfunktion, weil bei mir nur gesaugt wird.

> [!tip]
> Wer wischen will, lässt die `select.select_option`-Aktion einfach weg oder stellt sie auf den jeweilig sinnvollen Wert.

## 5. Die Todo-Liste: Status fürs Dashboard

Das Schöne an einem `todo`-Entity in *Home Assistant* ist, dass es als **interaktive Liste** auf dem Dashboard erscheint - inklusive Fälligkeitsdatum und Abhaken per Klick. Genau das wollte ich für meine Frau und mich: eine Anzeige, die zeigt, **was heute ansteht** und **was schon erledigt ist**.

> [!info]
> Klare Ansage vorweg, damit kein Missverständnis entsteht: die Todo-Liste ist in meinem Setup eine **reine Statusanzeige**, kein Steuer-Eingang. Die Trigger-Automation aus dem vorigen Kapitel liest sie **nicht**. Sie entscheidet allein anhand des Template-Sensors, welche Räume heute fällig sind. Die Todo-Liste wird also vom System **geschrieben**, aber nicht vom System **gelesen**.

Damit das Schreiben sauber funktioniert, kümmern sich zwei Automationen um die Liste: eine **nächtliche Aufbau-Automation** und eine **Abhak-Automation** nach erfolgreicher Reinigung.

### 5a. Nachts aufbauen

```yaml
alias: "Staubsauger: Todos bauen"
mode: single
trigger:
  - platform: time
    at: "00:05:00"
action:
  - action: input_boolean.turn_off
    target:
      entity_id: input_boolean.auto_vacuumed_today
  - action: todo.get_items
    data:
      status:
        - completed
    response_variable: result
    target:
      entity_id: todo.auto_vacuum
  - repeat:
      for_each: "{{ result['todo.auto_vacuum']['items'] }}"
      sequence:
        - variables:
            new_due: >
              {% set nxt = state_attr("binary_sensor.auto_vacuum_schedule", "next").areas %}
              {{ nxt.get(repeat.item.summary | area_id) }}
        - if:
            - condition: template
              value_template: "{{ new_due is not none }}"
          then:
            - action: todo.update_item
              data:
                item: "{{ repeat.item.summary }}"
                status: needs_action
                due_date: "{{ new_due | as_timestamp | timestamp_custom('%Y-%m-%d') }}"
              target:
                entity_id: todo.auto_vacuum
```

#### 5a. Erläuterungen

Diese Automation läuft um **fünf nach Mitternacht** und tut drei Dinge:

1. Sie setzt das `input_boolean.auto_vacuumed_today` auf `off`, damit der neue Tag offen ist.
2. Sie liest alle **abgeschlossenen Items** aus der Todo-Liste.
3. Für jedes dieser Items prüft sie im `next.areas`-Attribut des Template-Sensors, **wann der zugehörige Raum als Nächstes fällig** ist. Stimmt das Datum, wird das Item wieder auf `needs_action` gesetzt und das Fälligkeitsdatum aktualisiert.

Die Filter-Zauberei mit `repeat.item.summary | area_id` ist die Stelle, an der die Magie greift: weil der Todo-Item-Titel exakt dem Raumnamen entspricht (zum Beispiel *Wohnzimmer*), kann *Home Assistant* daraus automatisch die `area_id` ableiten. **Trennung der Zuständigkeiten**, schon wieder.

### 5b. Nach Reinigung abhaken

```yaml
alias: "Staubsauger: Todos abhaken"
mode: single
trigger:
  - platform: state
    entity_id: input_boolean.auto_vacuumed_today
    from: "off"
    to: "on"
action:
  - action: todo.get_items
    data:
      status:
        - needs_action
    response_variable: result
    target:
      entity_id: todo.auto_vacuum
  - repeat:
      for_each: "{{ result['todo.auto_vacuum']['items'] | selectattr('due', 'defined') | list }}"
      sequence:
        - if:
            - condition: template
              value_template: "{{ repeat.item.due | as_timestamp <= now().date() | as_timestamp }}"
          then:
            - action: todo.update_item
              data:
                item: "{{ repeat.item.summary }}"
                status: completed
              target:
                entity_id: todo.auto_vacuum
```

#### 5b. Erläuterungen

Diese Automation triggert exakt dann, wenn `input_boolean.auto_vacuumed_today` von `off` auf `on` springt - also genau nach einer erfolgreichen automatischen Reinigung. Sie holt sich alle **offenen** Items und hakt jene ab, deren `due`-Datum **heute oder früher** liegt. Das deckt sich exakt mit den Räumen, die der Sauger gerade abgefahren hat.

Der `input_boolean.auto_vacuumed_today` ist hier das **zentrale Signal**, das die drei Automationen miteinander koppelt: er wird von der Trigger-Automation gesetzt, von der Abhak-Automation gelesen und von der Aufbau-Automation nachts zurückgesetzt. **Ein Helfer, drei Rollen** - so soll das sein :smile:.

### Was die Todo-Liste konkret leistet

Ergebnis: jeden Morgen schaut meine Frau aufs Dashboard und sieht *"Wohnzimmer (heute), Flur (heute), Schlafzimmer (übermorgen)"*. Sobald der Roboter fertig ist, fliegen die heutigen Räume in den erledigt-Stapel. Nachts wird die Liste neu sortiert und mit aktuellen Fälligkeitsdaten versehen.

> [!note]
> Manuelles Abhaken eines Items hat in diesem Setup **keinen Einfluss** auf die Reinigung. Wer also "Wohnzimmer" morgens abhakt, weil er mit dem Akkusauger durch war, sieht zwar die Liste hübscher aussehen, der Roboter fährt das Wohnzimmer aber trotzdem an. Das ist eine bewusste Designentscheidung: die Logik *was wird gereinigt* lebt im Kalender, nicht in der Todo-Liste. Wenn ich das ändern möchte, ändere ich den Kalender, nicht ein Häkchen.

## Stolpersteine

Hier noch ein paar Beobachtungen aus dem Echtbetrieb:

**Helfer-Inflation.** Du brauchst mindestens drei Input-Booleans (`auto_vacuumed_today`, `vacation_mode`, plus eventuell weitere) und ein paar Input-Selects (`presence_mode`, `sleep_mode`). Das ist eine Menge Klick-Arbeit in der UI - lohnt sich aber, weil die Helfer in anderen Automationen wiederverwendet werden.

**Karten-Namen sind fragil.** Sobald du deine Roborock-Karte einmal umbenennst oder neu erstellst, ändern sich die Segment-IDs. Das Mapping im Script bleibt funktional, weil es über Namen läuft - aber genau hier liegt der Teufel im Detail.

**Roborock cloud vs. local.** Die offizielle [Roborock Integration](https://www.home-assistant.io/integrations/roborock/) nutzt die Roborock-Cloud. Wer das nicht mag - ich kann das gut nachvollziehen, Stichwort **Cloudzwang** - findet auf GitHub Alternativen mit lokalem Zugriff. Das Setup hier funktioniert mit beiden, weil es nur die Standard-Services aufruft.

**Wartezeit auf den Start.** Manche Roborock-Modelle brauchen nach dem `app_segment_clean`-Kommando einige Sekunden, bis sie überhaupt aus der Dock fahren. Mein Timeout von fünf Minuten ist großzügig gewählt - es reichen wahrscheinlich zwei. Probier es aus.

## Fazit

Was als zweijähriger Cliffhanger angefangen hat, ist heute eines der Setups, das mir im Alltag am wenigsten Aufmerksamkeit kostet und am meisten zurückgibt. Der Sauger läuft, **wenn er soll**. Er läuft **nicht**, wenn er nicht soll. Die Familie sieht den Status, ohne dass ich erklären müsste, was gerade automatisch passiert. **Stichwort:** *Women Acceptance Factor*, einmal mehr im grünen Bereich :grin:.

Der eigentliche Aha-Moment war für mich, dass der Reinigungsplan jetzt **textuell** in einem Kalender lebt. Wenn ich morgen entscheide, dass das Schlafzimmer dreimal die Woche dran sein soll, ändere ich genau eine `RRULE` und der Rest der Pipeline zieht von selbst nach. Keine Skript-Anpassung, keine YAML-Migration, kein Helfer-Karussell.

Vielleicht magst du das Setup für dein Zuhause adaptieren. Vielleicht hast du eine bessere Idee, wie man das alles noch eleganter löst - dann schreib mir gern. Bis dahin: **Viel Spaß** beim Reinigen lassen :sunglasses:!

---

Das Titel-/Hintergrundbild stammt von [YoonJae Baik](https://unsplash.com/de/@houston100?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash) auf [Unsplash](https://unsplash.com/de/fotos/weisser-kurzbeschichteter-kleiner-hund-auf-braunem-holzboden-p7MsAMLSbbU?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash).

