Tado + card_mod — Ein Heizungs-Dashboard, das etwas sagt

5 Tado-Zonen in Home Assistant mit Mushroom-Klimakarten, CSS-Atemanimation bei aktiven Zonen und anwesenheitsbasierter Absenkung, die wirklich funktioniert.

#tado #heizung #dashboard #card-mod #home-assistant
7. März 2025
Tado + card_mod — Ein Heizungs-Dashboard, das etwas sagt

Tados HA-Integration gehört zum Besten, was es out-of-the-box gibt — in HA integriert, kein HACS nötig. Du brauchst die Tado Internet Bridge Hardware und ein Tado-Konto. Integration hinzufügen, einloggen, und alle Zonen erscheinen als climate.*-Entitäten mit voller Unterstützung. Statusupdates kommen in unter fünf Sekunden zurück. Es funktioniert einfach.

Der Heizungsteil war also einfach. Das Interessante war, das Dashboard dazu zu bringen, auf einen Blick etwas zu sagen.

Die Atemanimation

Das Problem mit fünf Klimakarten in einem Raster: sie sehen alle gleich aus, egal ob die Heizung läuft oder nicht. Temperatur angezeigt, Sollwert angezeigt, Modus angezeigt. Aber man kann nicht sehen, welche Zonen gerade den Kessel befeuern.

Ich fügte eine CSS-Keyframe-Animation über card_mod hinzu, die nur aktiv wird, wenn das hvac_action-Attribut heating ist:

type: custom:mushroom-climate-card
entity: climate.zone_wohnzimmer
card_mod:
  style: |
    :host {
      {% if state_attr('climate.zone_wohnzimmer', 'hvac_action') == 'heating' %}
      animation: breathe 3s ease-in-out infinite;
      {% endif %}
    }
    @keyframes breathe {
      0%, 100% { filter: drop-shadow(0 0 4px rgba(255, 100, 0, 0.3)); }
      50%       { filter: drop-shadow(0 0 14px rgba(255, 100, 0, 0.75)); }
    }

Aktive Zonen pulsieren mit einem warmen orangefarbenen Leuchten. Inaktive sind statisch. Über ein 5-Zonen-Raster ist sofort klar, welche Räume Energie verbrauchen.

Ein Besucher fragte, ob das Haus atmet. Das fühlte sich richtig an.

Den Block für jede Zone duplizieren und die Entitäts-ID tauschen. Das Jinja2 innerhalb von card_mod wird zur Renderzeit ausgewertet, jede Karte entscheidet also selbstständig ob sie animiert.

Layout

Fünf Karten in einem 2-Spalten-Raster über custom:layout-card (HACS), eingewickelt in einen Glas-custom:stack-in-card (HACS), mit card_mod (HACS) für die CSS-Animationen:

type: custom:stack-in-card
card_mod:
  style: |
    ha-card {
      background: rgba(255,255,255,0.05) !important;
      backdrop-filter: blur(15px) !important;
      border: 1px solid rgba(255,255,255,0.10) !important;
      box-shadow: 0 8px 32px rgba(0,0,0,0.3) !important;
      border-radius: 28px !important;
    }
    ha-card::before { display: none !important; }
cards:
  - type: custom:layout-card
    layout_type: custom:grid-layout
    layout:
      grid-template-columns: 1fr 1fr
      grid-gap: 8px
      padding: 8px
    cards:
      - type: custom:mushroom-climate-card
        entity: climate.zone_wohnzimmer
        show_temperature_control: true
        card_mod:
          style: |
            :host {
              {% if state_attr('climate.zone_wohnzimmer', 'hvac_action') == 'heating' %}
              animation: breathe 3s ease-in-out infinite;
              {% endif %}
            }
            @keyframes breathe {
              0%, 100% { filter: drop-shadow(0 0 4px rgba(255,100,0,0.3)); }
              50%       { filter: drop-shadow(0 0 14px rgba(255,100,0,0.75)); }
            }
      # für jede Zone wiederholen

show_temperature_control: true fügt +/- Schaltflächen direkt auf der Karte hinzu. Kleines Detail, bedeutet aber, dass man eine Zone anpassen kann ohne irgendwo hinzunavigieren.

Anwesenheitsbasierte Absenkung

Wenn alle das Haus verlassen, alles auf 17°C absenken. Wenn jemand zurückkommt, Auto-Modus wiederherstellen, damit Tados eigener Zeitplan übernimmt:

alias: Tado — Abwesenheitsabsenkung
trigger:
  - platform: state
    entity_id: group.haushaltsmitglieder
    to: not_home
action:
  - repeat:
      for_each:
        - climate.zone_wohnzimmer
        - climate.zone_buerozimmer
        - climate.zone_schlafzimmer
        - climate.zone_badezimmer
        - climate.zone_flur
      sequence:
        - service: climate.set_temperature
          target:
            entity_id: "{{ repeat.item }}"
          data:
            temperature: 17

alias: Tado — Wiederherstellen bei Rückkehr
trigger:
  - platform: state
    entity_id: group.haushaltsmitglieder
    to: home
action:
  - repeat:
      for_each: [climate.zone_wohnzimmer, climate.zone_buerozimmer, climate.zone_schlafzimmer,
                 climate.zone_badezimmer, climate.zone_flur]
      sequence:
        - service: climate.set_hvac_mode
          target:
            entity_id: "{{ repeat.item }}"
          data:
            hvac_mode: auto

Ich nutze das statt Tados eigenem Geofencing, weil meine Anwesenheitserkennung GPS, UniFi-Geräteverfolgung und eine person.*-Entität kombiniert. Zuverlässiger als die Tado-App allein, die gelegentlich entscheidet, ich sei gegangen, obwohl ich nur im Garten bin.

Fenstererkennung

Tados Offenfenstererkennung ist hardware-basiert — der TRV misst einen plötzlichen Temperaturabfall und pausiert die Heizung für diese Zone automatisch. HA stellt das über binary_sensor.zone_*_window bereit.

Ich fügte eine Benachrichtigung hinzu, wenn ein Fenster länger als 20 Minuten bei aktiver Heizung offen ist. Das Badezimmerfenster ist der übliche Schuldige. Die Automatisierung hat wahrscheinlich mehr an Heizkosten gespart als alles andere, was ich rund um Tado gebaut habe.

alias: Tado — Offenes Fenster Warnung
trigger:
  - platform: state
    entity_id:
      - binary_sensor.zone_wohnzimmer_window
      - binary_sensor.zone_badezimmer_window
    to: "on"
    for:
      minutes: 20
condition:
  - condition: template
    value_template: >
      {{ state_attr(trigger.entity_id | replace('binary_sensor.', 'climate.') | replace('_window', ''), 'hvac_action') == 'heating' }}
action:
  - service: notify.all_devices
    data:
      title: "Fenster offen — Heizung läuft"
      message: >
        {{ trigger.entity_id
           | replace('binary_sensor.zone_','')
           | replace('_window','')
           | replace('_',' ')
           | title }} ist seit 20 Minuten offen.