Schlagwort-Archiv: Jacuzzi

Vom alten Pool zum Wellness-Deck

Wer meinen letzten Beitrag gelesen hat, weiß: Unser Bestway Lay-Z-Spa Cabo ist voll in Home Assistant integriert und heizt quasi kostenlos mit PV-Überschuss, doch was noch fehlte war die Beschreibung wie diese Wellness Oase entstanden ist.

Bis Anfang dieses Jahres hatten wir jahrelang einen klassischen Gartenpool in unterschiedlichen Ausführungen. Da die Kinder ausgezogen sind, stand der Pool am Ende fast nur noch ungenutzt herum, machte aber jede Menge Arbeit. Die Entscheidung stand fest: Der Pool fliegt raus und stattdessen bauen wir an der gleichen Stelle ein großes, gemütliches Holzdeck zum Relaxen.

Und weil man nach handwerklicher Arbeit im Garten oder zum Abkühlen im Sommer einen Platz braucht, sollte zusätzlich zu dem neuen Holzdeck noch ein Jacuzzi und eine 40-Liter-Solardusche Platz finden.

Hier ist der ungeschönte Baubericht von den Erdarbeiten über die statischen Tücken eines Sandkastens bis hin zur perfekten millimetergenauen Anpassung an die Rasenkanten.


Wellness

📸 Aus alt mach neu


🛠️ Phase 1: Die Abriss- und Erdarbeiten (Eine Heidenarbeit)

Der Platz, den ich mir für das neue Holzdeck ausgesucht hatte, war ein alter, gepflasterter Bereich von ca. 10 m². Schön war er schon lange nicht mehr und genutzt wurde er dementsprechend auch nicht. Den Jacuzzi einfach auf die alten Beton-Pflastersteine zu stellen, kam für mich überhaupt nicht infrage – das hätte optisch einfach nichts hergemacht und wäre spätestens bei der Montage der Solardusche zu einem echten Problem geworden.

  • Schritt 1: Die kompletten Beton-Pflastersteine auf den 10 m² herausreißen. Wer schon mal Betonpflaster geschleppt hat, weiß, wie verdammt schwer die Dinger sind.
  • Schritt 2: Darunter lag eine 2 bis 4 cm dicke Schicht alter Split. Auch die musste komplett weichen.

Als der Split weg war, kam eine Überraschung zum Vorschein: Der Großteil des Platzes war darunter komplett betoniert! Zuerst dachte ich an eine alte Sickergrube, doch meine Schwiegermutter brachte Licht ins Dunkel: Früher war hier einmal ein Freisitz.

Das Sandkasten-Problem in der Mitte

Die eigentliche Schmerzstelle zeigte sich genau in der Mitte dieses alten Freisitzes: Eine exakte Aussparung im Beton von 1,40 m × 1,40 m. Hier war früher der Sandkasten! Heute war das Loch einfach mit Erde gefüllt.

Statisch gesehen der absolute Albtraum für ein Whirlpool-Fundament. Ein gefüllter Jacuzzi wiegt rund 1,1 Tonnen auf einer winzigen Fläche. Hätte ich meine Tragkonstruktion einfach auf dieses Erdloch gestellt, wären die Stelzlager über kurz oder lang im weichen Boden eingesunken. Die Terrasse hätte sich unweigerlich verzogen.

Die Lösung: Ich habe das Erdloch gut 9 cm tief ausgehoben, eine Schicht frischen Split eingebracht und verdichtet. Darauf habe ich dann die alten Pflastersteine, die ich vorher herausgerissen hatte, im Loch sauber neu verlegt. Das Ergebnis: Eine durchgehend stabile, tragfähige Ebene aus Beton und Pflaster.

📐 Phase 2: Die Unterkonstruktion – Statik trifft auf Gefälle

Da der Untergrund nach vorne hin leicht abschüssig war, stand ich vor der nächsten Herausforderung. Das Deck musste am Ende absolut waagerecht sein – besonders für den Whirlpool, da das Wasser sonst schief im Becken steht.

Aus diesem Grund kamen zwei verschiedene Größen von verstellbaren Stelzlagern (Balkenlager PRO von gartenwelt_riegelsberger) zum Einsatz:

  • Im hinteren (höheren) Bereich: Größe 27–40 mm (90 Stück)
  • Im vorderen (tieferen) Bereich zum Ausgleichen: Größe 40–64 mm (40 Stück)

Insgesamt knapp 130 Stelzlager sorgen nun dafür, dass das Deck perfekt in Waage liegt.

Warum so viele Stelzlager und so enge Abstände?

Vielleicht fragst du dich, warum man für ein 10 m² Deck so extrem viele Stelzlager und Balken braucht. Die Antwort liegt wieder im enormen Gewicht des Jacuzzis.

Normalerweise verlegt man die Unterkonstruktion bei Terrassendielen in einem Abstand von 40 bis 50 cm. Unter dem Whirlpool habe ich das Raster jedoch massiv verdichtet und die Balken (Kiefer KDI, 45 × 70 mm, flach hingelegt für eine optimale Auflagefläche) alle 20 cm gesetzt! Dadurch verteilt sich das Gewicht des Pools auf unzählige Kreuzungspunkte. Auf jedes einzelne Stelzlager drückt am Ende nur ein kleiner Bruchteil des Gewichts. Hier biegt sich garantiert nichts durch.

🚫 Warum wir die teuren Abstandshalter nicht verbaut haben

Im Laufe der Planung hatte ich überlegt, spezielle Terrassendielen-Abstandshalter (Kunststoff-Clips zwischen Balken und Diele) zu kaufen. Bei über 500 benötigten Kreuzungspunkten hätte ich jedoch locker 600 bis 700 Stück kaufen müssen. Das stand finanziell in absolut keinem Verhältnis mehr zum Nutzen.

Wir haben das Ganze handwerklich clever und extrem günstig gelöst:

  1. Konstruktiver Holzschutz durch Gefälle: Nach hinten wird das Deck von einer Mauer begrenzt, nach vorne schließt es an bestehende Rasenkantensteine an. Die Unterkonstruktion wurde so ausgerichtet, dass das Wasser durch ein minimales Gefälle in den Fugen ablaufen kann und keine Staunässe zwischen Dielen und Balken entsteht.
  2. Fugenkeile statt Dauerelemente: Um beim Verschrauben der Douglasie-Dielen (2500 × 124 × 21 mm) überall exakt die wichtigen 5 mm Fugenbreite einzuhalten, kamen einfache, wiederverwendbare Kunststoff-Steckkreuze aus dem Baumarkt zum Einsatz. Nach dem Festschrauben zieht man sie einfach wieder raus – Kostenpunkt: keine 10 Euro.

🚿 Das Statik-Highlight: Die Solardusche bombenfest montiert

Ein weiteres wichtiges Element auf dem Deck ist die CCLIFE Solardusche mit 40 Litern Tankvolumen. Vollgefüllt bringt das gute Stück gut 45 bis 50 kg auf die Waage. Da die Dusche über 2 Meter hoch ist, wirken durch Hebelkräfte (Wind, Festhalten beim Duschen) enorme Kräfte auf den Standfuß. Schraubt man diese einfach nur in die 21 mm dünnen Dielen, reißen die Schrauben beim ersten Sturm aus.

Glücklicherweise lag genau an der Stelle, wo die Dusche hin sollte, ein Unterkonstruktionsbalken darunter. Ich habe direkt unter diese Stelle zwei zusätzliches Stelzlager gesetzt, um den Druck nach unten abzufangen und auch noch 3 Querbalken eingefügt.

Der Schrauben-Größen-Krimi

Die Dielen und flach liegenden Balken haben zusammen eine Stärke von 66 mm (21 mm Diele + 45 mm Balken). Der Standfuß der Dusche schlägt noch einmal mit 10 mm zu Buche. Macht eine Gesamttiefe von 76 mm.

Da es im Baumarkt keine 70 mm Schrauben in Edelstahl A2 gab, musste ich zu 80 mm Sechskant-Schlüsselschrauben greifen. Um zu verhindern, dass die Schraube unten aus dem Balken herausschaut und das darüberliegende Kunststoff-Stelzlager zerstört, habe ich einen einfachen Handwerkertrick angewendet: Ich habe unter jeden Schraubenkopf am Fuß der Dusche 3 dicke Edelstahl-Unterlegscheiben (M8) gestapelt. Dadurch wanderte die Schraube ca. 5 mm nach oben, sitzt perfekt tief im Holz und das Stelzlager bleibt absolut unberührt.

📐 Phase 3: Die millimetergenauen Randarbeiten

Eine Terrasse wirkt erst dann richtig professionell, wenn die Anschlüsse an die Umgebung stimmen. Da der Platz zu den Seiten hin nicht perfekt rechtwinklig verlief, musste an den Rändern Maßarbeit her:

  • Die schräge Seite: Hier habe ich im Zuge des Baus neue Rasenkantensteine gesetzt. Jede einzelne Douglasie-Diele wurde dann auf dieser Seite mit der Kappsäge im exakten Winkel so zugeschnitten, dass sie gut mit dem Kantenstein abschließt.
  • Die andere (gerade) Seite: Auch hier war der Verlauf der alten Pflastersteine ungenau. Ich habe zuerst die Terrassendielen komplett verlegt und danach Teile des angrenzenden Pflasters entfernt. Am Schluss habe ich die Pflastersteine mit dem Winkelschleifer und einer Diamanttrennscheibe so exakt zugeschnitten, dass sie nun nahtlos und ohne hässliche Lücken an die Dielen anstoßen.

📋 Materialliste: Was wurde für das Projekt benötigt?

Für alle, die ein ähnliches Projekt planen, habe ich hier noch einmal das gesamte benötigte Material und die wichtigsten Werkzeuge zusammengefasst:

Holz & Unterkonstruktion

  • Terrassendielen: Douglasie-Dielen (Maße: 2500 × 124 × 21 mm)
  • Unterkonstruktionsbalken: Kiefer KDI (Maße: 45 × 70 mm), flach verlegt für optimale Lastenverteilung

Fundament & Ausgleich

  • Stelzlager hinten (niedrig): 90x Balkenlager PRO Stellfuß (Größe 27–40 mm) von gartenwelt_riegelsberger
  • Stelzlager vorne (hoch): 40x Balkenlager PRO Stellfuß (Größe 40–64 mm) von gartenwelt_riegelsberger
  • Erdlochausgleich: Frischer Split (zum Verdichten) & vorhandene alte Beton-Pflastersteine
  • Randabschluss: Neue Rasenkantensteine für die schräge Seite

Schrauben & Montagezubehör

  • Dielenschrauben: Terrassenschrauben aus Edelstahl A2 mit Torx-Antrieb (Größe 5 × 50 mm oder 5 × 60 mm)
  • Duschbefestigung: 4x Sechskant-Holzschrauben / Schlüsselschrauben aus Edelstahl A2 (Größe 8 × 80 mm)
  • Unterlegscheiben: 12x dicke Edelstahl-Unterlegscheiben (M8) zum Höhenausgleich unter den Dusch-Schraubenköpfen
  • Fugenhelfer: Wiederverwendbare Fliesen-/Steckkreuze aus Kunststoff (5 mm Fugenbreite)

Wellness-Ausstattung

  • Bestway LAY-Z-SPA Energiespar-Whirlpool Cabo HydroJet
  • CCLIFE Solardusche mit 40 Litern Tankvolumen

💎 Fazit: Unsere ganz persönliche Wohlfühloase

Der Aufwand hat sich definitiv gelohnt. Die Kombination aus dem warmen Holzdeck, dem perfekt eingelassenen Jacuzzi und der Solardusche hat den ehemals hässlichen Freisitz komplett verwandelt. Dank der extrem dichten Unterkonstruktion steht der Whirlpool absolut sicher, und die Solardusche übersteht dank des Schrauben-Tricks auch den stärksten Windstoß.

Wenn du wissen willst, wie ich diesen Jacuzzi jetzt so automatisiert habe, dass er komplett kostenlos mit Solarstrom aufheizt, schau dir unbedingt meinen Artikel zur Home Assistant Jacuzzi-Integration an!

Hast du Fragen zum Unterbau, den Stelzlagern oder dem statischen Aufbau? Schreib es mir gerne unten in die Kommentare! 👇

Und ganz am Ende noch das Wichtigste … macht Euch vorher einen Plan von dem was ihr vorhabt, dann könnt ihr sehr sicher sein, dass ihr an „fast“ alles gedacht habt!

Wellness-Oase-Garten mit HA Integration (Bestway)

Wer einen Garten hat, kennt das Gefühl: Nach einem langen Tag einfach in den Jacuzzi sinken, warmes Wasser, Ruhe – pure Erholung. Doch bis es so weit ist, wartet man entweder ewig auf die richtige Wassertemperatur, oder man verschwendet Energie, weil der Whirlpool stundenlang unnötig heizt.

Ich habe das Problem gelöst – mit Home Assistant. In diesem Beitrag zeige ich dir, wie ich meinen Bestway Jacuzzi und meine Gartendusche vollständig automatisiert habe. Der Jacuzzi heizt nur dann, wenn genug Photovoltaik-Überschuss vorhanden ist – also praktisch kostenlos. Die Dusche schaltet sich nach einer konfigurierbaren Zeit automatisch ab. Beides lässt sich bequem per Dashboard steuern und überwachen.

Und wen es interessiert , es gibt noch einen Blogbeitrag, wie das Holzdeck und die Dusche entstanden sind ➡️ Vom alten Pool zum Wellness-Deck


📸 Dashboard der Wellness-Oase im Garten mit Jacuzzi und Dusche

🛁 Teil 1: Der smarte Jacuzzi


🔧 Ausgangsproblem

Der Bestway Lay-Z-Spa bringt von Haus aus keine nennenswerte Automatisierung mit. Man schaltet ihn ein, stellt die Zieltemperatur ein – und wartet. Das Aufheizen dauert je nach Ausgangstemperatur mehrere Stunden und kostet entsprechend Strom.

👉 Meine Probleme waren:

  • Heizen kostet Strom – idealerweise sollte das nur bei PV-Überschuss passieren
  • Wasser muss regelmäßig gefiltert werden – auch wenn die Heizung gerade nicht läuft
  • War der Jacuzzi manuell eingeschaltet, sollte die Automatisierung das nicht ignorieren oder gar den Spa unkontrolliert ausschalten

🔗 Die Bestway-Integration für Home Assistant

Die Grundlage für die gesamte Steuerung ist die Bestway-Integration für Home Assistant, die den Jacuzzi über die Cloud-API von Bestway einbindet. Die Installation erfolgt über HACS.

📸 Screenshot der Bestway-Integration

⚠️ Wichtiger Hinweis zum Modell: Mein konkretes Modell, der Bestway LAY-Z-SPA Energiespar-Whirlpool Cabo HydroJet, ist in der offiziellen Liste der unterstützten Geräte nicht aufgeführt. Die Integration funktioniert dennoch – jedoch nicht vollständig.

Das grundsätzliche Problem: Mehrere Entitäten liefern über die API keine zuverlässige Rückmeldung über den tatsächlichen Gerätezustand. Befehle lassen sich senden – ob sie angekommen sind und was das Gerät jetzt wirklich macht, meldet die API aber nicht immer zurück. Besonders deutlich wird das an der Filterpumpe: Hier musste ich sogar auf einen Mechanismus mit drei Ausschaltversuchen zurückgreifen, weil ein einzelner Befehl nicht garantiert zuverlässig ausgeführt wird.

Überall wo die API-Rückmeldung nicht verlässlich ist, habe ich denselben Workaround-Ansatz gewählt: interne Booleans als Zustandsspeicher, die von den Automationen selbst gesetzt werden – unabhängig davon was die API zurückmeldet. Das bekannteste Beispiel ist der Template-Switch switch.v8_jacuzzi_spa_filter_ui in Kombination mit input_boolean.v8_jacuzzi_filter_state: Der Boolean ist die verlässliche Zustandsquelle für das Dashboard, nicht die API-Entität direkt.

Wer denselben oder einen ähnlichen Bestway-Jacuzzi besitzt und mit ähnlichen API-Problemen kämpft: Dieser Ansatz – eigene Zustandsspeicher statt API-Feedback – hat sich bei mir bewährt. Schreib gerne in die Kommentare welches Modell du hast und welche Erfahrungen du gemacht hast.


🎯 Ziele meiner Automatisierung

  • ☀️ Jacuzzi heizt nur bei ausreichendem PV-Überschuss (≥ 2 kW für 2 Minuten)
  • ⏹️ Automatisches Abschalten wenn Überschuss zu gering (< 1 kW für 3 Minuten)
  • ⏱️ Mindestlaufzeit von 30 Minuten schützt vor ständigem Ein-/Ausschalten
  • 💧 Regelmäßige Filterzyklen – unabhängig davon ob die Heizung läuft
  • 🧠 War der Spa manuell eingeschaltet: Automatisierung greift nur in Heizung und Filter ein – Spa bleibt an
  • 📧 E-Mail-Benachrichtigungen bei jedem automatischen Start, Stopp und bei Fehlern
  • 📊 Übersichtliches Dashboard zur Steuerung und Statusanzeige
  • 🌡️ Zieltemperatur erreicht → alles automatisch ausschalten, Neustart erst wenn das Wasser 2 °C abgekühlt ist und PV-Überschuss wieder ausreicht
  • 💧 Regelmäßige Filterzyklen – unabhängig davon ob die Heizung läuft

  • 🔌 Voraussetzungen – benötigte Entitäten

    Diese Entitäten werden durch die Bestway-Integration in Home Assistant bereitgestellt und müssen bereits existieren:

    • switch.v8_jacuzzi_spa_power – Hauptschalter des Spas
    • switch.v8_jacuzzi_spa_filter – Filterpumpe
    • climate.v8_jacuzzi_spa_thermostat – Heizungssteuerung
    • binary_sensor.v8_jacuzzi_spa_connected – Verbindungsstatus
    • binary_sensor.v8_jacuzzi_spa_errors – Fehlererkennung
    • sensor.em_available_for_heatpump – PV-Überschuss aus dem Energy Manager (mehr dazu weiter unten)

    ⚡ Zusammenspiel mit dem Energy Manager

    Das Herzstück der PV-Steuerung ist der Sensor sensor.em_available_for_heatpump aus meinem Energy Manager Package. Dieser Sensor berechnet in Echtzeit, wie viel Solarstrom tatsächlich für weitere Verbraucher verfügbar ist:

    Verfügbar = PV-Leistung (geglättet) - Haushaltslast (geglättet) - Powerwall-Ladung

    Der entscheidende Vorteil: Läuft meine Wärmepumpe gerade, ist ihr Verbrauch bereits in der Haushaltslast enthalten. Der Sensor sinkt automatisch. Der Jacuzzi startet dann nur noch bei echtem Zusatzüberschuss – kein separater Konfliktschutz zwischen Wärmepumpe und Jacuzzi nötig.


    🧩 Helper – automatisch durch das Package erstellt

    Das Package erstellt beim Start alle benötigten Helfer automatisch. Kein manuelles Anlegen notwendig.

    🔘 Steuer-Flags

    • input_boolean.jacuzzi_pv_auto – PV-Heizautomatik ein/aus (Standard: an). Perfekt für den Urlaubsfall.
    • input_boolean.jacuzzi_filter_auto – Filter-Automatik ein/aus (Standard: an). Unabhängig von der Heizautomatik.
    • input_boolean.jacuzzi_spa_started_by_pv – Merkt sich ob der Spa durch die PV-Automatik eingeschaltet wurde. Verhindert, dass ein manuell eingeschalteter Spa automatisch wieder ausgeschaltet wird.
    • input_boolean.jacuzzi_temp_reached – Merkt sich ob der letzte Stopp durch das Erreichen der Zieltemperatur ausgelöst wurde. Verhindert, dass der Spa sofor t neu startet, bevor das Wasser 2 °C abgekühlt ist.

    ⚙️ Schwellenwerte & Konfiguration

    • input_number.jacuzzi_pv_start – PV-Überschuss zum Einschalten (Standard: 2,0 kW)
    • input_number.jacuzzi_pv_stop – PV-Überschuss zum Abschalten (Standard: 1,0 kW)
    • input_number.jacuzzi_target_temp – Zieltemperatur (Standard: 37 °C)
    • input_number.jacuzzi_min_runtime – Mindestlaufzeit vor automatischem Stopp (Standard: 30 Min)
    • input_number.jacuzzi_filter_duration – Dauer eines Filterzyklus (Standard: 60 Min)

    🕐 Filterzeiten

    • input_datetime.jacuzzi_filter_time_1 – Erste tägliche Filterzeit (z. B. 08:00 Uhr)
    • input_datetime.jacuzzi_filter_time_2 – Zweite tägliche Filterzeit (z. B. 20:00 Uhr)

    💡 Tipp: Die Heizphasen tagsüber liefern automatisch zusätzliche Filterzeit – die zwei konfigurierten Zyklen dienen als Mindestabsicherung für Tage ohne PV-Überschuss.


    🤖 Die 8 Automationen im Überblick

    1️⃣ PV-Überschuss EIN

    Sobald der verfügbare PV-Überschuss die Start-Schwelle (Standard: 2 kW) für mindestens 2 Minuten überschreitet, startet der Jacuzzi automatisch. Der Debounce von 2 Minuten verhindert, dass kurze Schwankungen im Energieangebot den Spa ständig ein- und ausschalten.

    👉 Besonderheit: War der Spa bereits manuell eingeschaltet, schaltet die Automatisierung ihn nicht nochmal ein – sie setzt nur Heiztemperatur und Filter. Der jacuzzi_spa_started_by_pv-Boolean merkt sich dabei, ob die Automatisierung den Spa selbst gestartet hat.

    2️⃣ PV-Überschuss AUS

    Fällt der Überschuss für 3 Minuten unter die Stop-Schwelle (Standard: 1 kW), schaltet die Automatisierung Heizung und Filter ab. Zusätzlich greift eine Mindestlaufzeit: Der Spa läuft immer mindestens 30 Minuten, bevor er automatisch gestoppt werden kann.

    👉 War der Spa vor dem PV-Start bereits manuell eingeschaltet, wird er nicht ausgeschaltet – nur Heizung und Filter stoppen.

    3️⃣ Filter-Zyklus

    Täglich zu den zwei konfigurierten Zeiten startet automatisch ein Filterzyklus – unabhängig davon ob die Heizung gerade läuft. Die Automation ist dabei smart:

    • Spa war aus → Spa kurz einschalten, Filter starten, nach Ablauf der Filterdauer: Filter stoppen, Spa wieder ausschalten (außer die Heizung ist inzwischen aktiv)
    • Spa war bereits an → nur Filter starten und nach Ablauf stoppen
    • Filter läuft bereits (z. B. wegen Heizung) → Zyklus wird übersprungen

    4️⃣ Auto-Modus deaktiviert

    Schaltet man jacuzzi_pv_auto manuell aus während der Spa läuft, stoppt diese Automation sauber Heizung und Filter. Den Spa selbst schaltet sie nur aus wenn er ursprünglich durch die PV-Automatik gestartet wurde – war er manuell eingeschaltet, bleibt er an.

    5️⃣ Fehler-Benachrichtigung

    Meldet der Spa einen Fehler (z. B. den gefürchteten E02 bei verstopfter Filterkartusche), kommt sofort eine E-Mail-Benachrichtigung und ein Eintrag im System-Log.

    💡 Tipp zur Filterpflege: Die Filterkartusche sollte spätestens alle 2–3 Tage gereinigt werden, sonst kommt es schnell zu Fehlern durch schlechten Durchfluss.

    6️⃣ Zieltemperatur sofort übernehmen

    Ändert man die Zieltemperatur im Dashboard während der Spa bereits läuft, soll die neue Temperatur sofort an den Thermostat weitergegeben werden – ohne auf den nächsten automatischen Start warten zu müssen.

    Diese Automation überwacht input_number.jacuzzi_target_temp auf Änderungen. Ist der Spa zu diesem Zeitpunkt eingeschaltet, wird die neue Temperatur unmittelbar per climate.set_temperature gesetzt. Ist der Spa aus, passiert nichts – beim nächsten Einschalten greift dann wie gewohnt die PV-EIN-Automation.

    7️⃣ Zieltemperatur erreicht – alles ausschalten

    Damit der Jacuzzi nicht eingeschaltet bleibt, wenn die Zieltemperatur erreicht ist und unnötiger Stromverbrauch verbraucht wird, gibt es diese zusätzliche Automatisierung.

    Diese neue Automation löst das Problem mit einer Hysterese-Regelung: Sobald climate.v8_jacuzzi_spa_thermostat in den Zustand idle wechselt (= Zieltemperatur ist erreicht), schaltet die Automation alles ab: Heizung off, Filter 3× off, Spa Power off. Gleichzeitig wird der interne Merker jacuzzi_temp _reached gesetzt.

    👉 Die Automation greift nur wenn jacuzzi_pv_auto aktiv ist – bei manuellem Betrieb bleibt das normale Thermostat-Verhalten (heat → idle → heat) erhalten.

    8️⃣ Temperatur gesunken – automatischer Neustart

    Das Gegenstück zu Automation 7: Sobald die Wassertemperatur um 2 °C unter die Zieltemperatur gefallen ist – und das für mindestens 1 Minute stabil bleibt – startet der Jacuzzi automatisch neu, sofern alle Bedingungen erfüllt sind:

    • PV-Auto ist aktiv
    • Der Merker jacuzzi_temp_reached ist gesetzt (kein versehentlicher Neustart aus anderen Szenarien)
    • Spa ist verbunden und fehlerfrei
    • PV-Überschuss ist wieder ausreichend (≥ Start-Schwelle)

    💡 Wichtig: Reicht der PV-Überschuss beim 2-°C-Abfall noch nicht aus, bleibt der Spa aus und wartet. Sobald der Überschuss wieder steigt und die Temperatur noch 2 °C unter Ziel liegt, springt entweder diese Automation an – oder die normale PV-EIN-Automation übernimmt. Ein Doppelstart wird durch den jacuzzi_temp_reached-Merker sicher verhindert.


    🖥️ Das Jacuzzi-Dashboard

    Das Jacuzzi-Dashboard ist ein extra Dashboard aufgebaut und nutzt das Theme Nordic. Es ist über eine klickbaren Heading direkt aus meinem Hauptdashboard ereichbar. Alle Buttons basieren auf custom:button-card.

    📸 Screenshot des Jacuzzi-Dashboard

    Steuer-Buttons

    Die Steuer-Buttons sind in einem 2-Spalten-Grid angeordnet. Jeder Button zeigt Icon, Name und Zustand in einer kompakten 60px-Karte:

    • Jacuzzi Power – grün wenn an, dunkel wenn aus. Tippen schaltet den Spa direkt um.
    • Heizung – rot bei aktiver Heizung, orange im Idle-Modus (Zieltemp erreicht, Thermostat wartet), dunkel wenn aus. Tippen ruft das Script jacuzzi_heat_toggle auf, das Heizung und Filter gemeinsam schaltet.
    • Filterpumpe – blau wenn aktiv, dunkel wenn aus. Arbeitet über den Template-Switch switch.v8_jacuzzi_spa_filter_ui als verlässliche Zustandsquelle (siehe API-Hinweis oben).
    • Bubble-Modus – orange wenn MAX, dunkel wenn OFF. Tippen wechselt direkt zwischen OFF und MAX.
    • Massagefunktion – lila wenn aktiv, dunkel wenn aus.
    📸 Die fünf Steuer-Buttons – verschiedene Zustände sichtbar

    📌 Übersichts-Widget auf dem Hauptdashboard

    Auf meinem zentralen Tablet-Dashboard habe ich zusätzlich ein kompaktes 4-Button-Widget eingebaut, das auf einen Blick den wichtigsten Status von Jacuzzi und Dusche zeigt – ohne in die Detailansicht wechseln zu müssen. Die vier Buttons sind in einem 2-Spalten-Grid angeordnet:

    • Jacuzzi Power – Hauptschalter, grün wenn an
    • Jacuzzi Status – Der Button zeigt z. B. „Heizung AN · Filter AN · Massage AN“ wenn mehrere Funktionen aktiv sind, oder schlicht „Alles aus“. Er reagiert nicht auf Tippen – er dient nur der Anzeige. Die Kartenfarbe wechselt zu Blau sobald mindestens eine Funktion aktiv ist.
    • Dusche – Ventilstatus, „Ein“ oder „Aus“
    • Jacuzzi Heizung – zeigt Ist- und Solltemperatur direkt im Button: „Ist: 34°C / Soll: 37°C“ – so sieht man sofort wie weit der Jacuzzi noch aufheizen muss

    Der Heizungs-Button ist dabei besonders praktisch: Dank einem state_display-Template werden Ist- und Zieltemperatur direkt in der Statuszeile angezeigt. Kein Aufklappen, kein More-Info nötig.

    📸 4-Button-Übersichts-Widgets auf dem Hauptdashboard

    ⚡ Stromverbrauch im Blick

    Auf der Jacuzzi-Detailseite habe ich drei Verbrauchs-Anzeigen eingebaut, damit ich jederzeit sehe was der Jacuzzi gerade und in den letzten Tagen verbraucht hat:

    • Gauge – aktueller Verbrauch: Zeigt die aktuelle Leistungsaufnahme in Watt an. Farblich abgestuft: grün (Normalbetrieb), gelb (erhöht), rot (Volllast).
    • Stündliche Übersicht (24h): Ein Balkendiagramm auf Stundenbasis zeigt den Leistungsverlauf der letzten 24 Stunden – ideal um zu sehen, wann die Heizphasen und Filterzyklen stattgefunden haben.
    • 14-Tage-Übersicht: Ein Tagesbalken-Diagramm der letzten 14 Tage zeigt den täglichen Energieverbrauch in kWh. Auf einen Blick sieht man, an welchen Tagen der Jacuzzi viel oder wenig PV-Überschuss genutzt hat.
    📸 Verbrauchsüberischt

    📋 Der YAML-Code

    Helper & Automationen (config/packages/jacuzzi.yaml)

    # config/packages/jacuzzi.yaml
    
    # --- Helpers ---
    input_boolean:
      jacuzzi_pv_auto:
        name: "Jacuzzi: PV-Auto aktiv"
        initial: on
        icon: mdi:hot-tub
    
      jacuzzi_filter_auto:
        name: "Jacuzzi: Filter-Automatik aktiv"
        initial: on
        icon: mdi:water-sync
    
      v8_jacuzzi_filter_state:
        name: "Jacuzzi Filter Status"
        initial: off
        icon: mdi:water-pump
    
      jacuzzi_spa_started_by_pv:
        name: "Jacuzzi Spa durch PV gestartet"
        initial: off
        icon: mdi:solar-power
    
      jacuzzi_temp_reached:
        name: "Jacuzzi Zieltemperatur erreicht (Pause)"
        initial: off
        icon: mdi:thermometer-check
    
    input_number:
      jacuzzi_pv_start:
        name: "Jacuzzi PV Start-Schwelle (kW)"
        initial: 2.0
        min: 0.5
        max: 6.0
        step: 0.1
        unit_of_measurement: kW
    
      jacuzzi_pv_stop:
        name: "Jacuzzi PV Stop-Schwelle (kW)"
        initial: 1.0
        min: 0.0
        max: 5.0
        step: 0.1
        unit_of_measurement: kW
    
      jacuzzi_target_temp:
        name: "Jacuzzi Zieltemperatur (°C)"
        initial: 37
        min: 20
        max: 40
        step: 0.5
        unit_of_measurement: "°C"
    
      jacuzzi_min_runtime:
        name: "Jacuzzi Mindestlaufzeit (Min)"
        initial: 30
        min: 5
        max: 120
        step: 5
    
      jacuzzi_filter_duration:
        name: "Jacuzzi Filter Laufzeit (Min)"
        initial: 60
        min: 15
        max: 240
        step: 15
        unit_of_measurement: min
    
    # --- Filter-Zeitsteuerung ---
    input_datetime:
      jacuzzi_filter_time_1:
        name: "Jacuzzi Filter Zeit 1"
        has_time: true
        has_date: false
    
      jacuzzi_filter_time_2:
        name: "Jacuzzi Filter Zeit 2"
        has_time: true
        has_date: false
    
    # --- Automationen ---
    automation:
      - alias: "Jacuzzi: PV-Überschuss EIN"
        description: "Schaltet Jacuzzi-Heizung bei ausreichendem PV-Überschuss ein"
        mode: single
        trigger:
          - platform: numeric_state
            entity_id: sensor.em_available_for_heatpump
            above: input_number.jacuzzi_pv_start
            for: "00:02:00"
        condition:
          - condition: state
            entity_id: input_boolean.jacuzzi_pv_auto
            state: "on"
          - condition: state
            entity_id: binary_sensor.v8_jacuzzi_spa_connected
            state: "on"
          - condition: state
            entity_id: binary_sensor.v8_jacuzzi_spa_errors
            state: "off"
          - condition: template
            value_template: >
              {{ not is_state('input_boolean.jacuzzi_temp_reached', 'on') or
                 state_attr('climate.v8_jacuzzi_spa_thermostat', 'current_temperature') | float(100)
                 < (states('input_number.jacuzzi_target_temp') | float(37) - 2) }}
        action:
          - variables:
              spa_was_off: "{{ is_state('switch.v8_jacuzzi_spa_power', 'off') }}"
          - service: input_boolean.turn_off
            target:
              entity_id: input_boolean.jacuzzi_temp_reached
          - if:
              - condition: template
                value_template: "{{ spa_was_off }}"
            then:
              - service: switch.turn_on
                target:
                  entity_id: switch.v8_jacuzzi_spa_power
              - service: input_boolean.turn_on
                target:
                  entity_id: input_boolean.jacuzzi_spa_started_by_pv
              - delay: "00:00:05"
          - service: climate.set_temperature
            target:
              entity_id: climate.v8_jacuzzi_spa_thermostat
            data:
              temperature: "{{ states('input_number.jacuzzi_target_temp') | float(37) }}"
          - service: climate.set_hvac_mode
            target:
              entity_id: climate.v8_jacuzzi_spa_thermostat
            data:
              hvac_mode: heat
          - service: switch.turn_on
            target:
              entity_id: switch.v8_jacuzzi_spa_filter
          - service: input_boolean.turn_on
            target:
              entity_id: input_boolean.v8_jacuzzi_filter_state
          - service: system_log.write
            data:
              message: >
                Jacuzzi: EIN via PV-Überschuss
                {{ states('sensor.em_available_for_heatpump') }} kW,
                Zieltemp {{ states('input_number.jacuzzi_target_temp') }}°C
                (Spa war {{ 'aus – wurde eingeschaltet' if spa_was_off else 'bereits an' }})
              level: info
          - data:
              title: "Jacuzzi gestartet"
              message: >
                Jacuzzi automatisch gestartet
                (PV-Überschuss: {{ states('sensor.em_available_for_heatpump') }} kW,
                Zieltemp: {{ states('input_number.jacuzzi_target_temp') }}°C)
            action: notify.kottlowski_gmail_com
    
      - alias: "Jacuzzi: PV-Überschuss AUS"
        description: "Schaltet Jacuzzi-Heizung ab wenn PV-Überschuss zu gering"
        mode: single
        trigger:
          - platform: numeric_state
            entity_id: sensor.em_available_for_heatpump
            below: input_number.jacuzzi_pv_stop
            for: "00:03:00"
        condition:
          - condition: state
            entity_id: input_boolean.jacuzzi_pv_auto
            state: "on"
          - condition: state
            entity_id: switch.v8_jacuzzi_spa_power
            state: "on"
          - condition: template
            value_template: >
              {% set last_on = states.switch.v8_jacuzzi_spa_power.last_changed %}
              {% set min_run = states('input_number.jacuzzi_min_runtime') | int(30) %}
              {{ (as_timestamp(now()) - as_timestamp(last_on)) > (min_run * 60) }}
        action:
          - service: climate.set_hvac_mode
            target:
              entity_id: climate.v8_jacuzzi_spa_thermostat
            data:
              hvac_mode: "off"
          - delay: "00:00:02"
          - service: switch.turn_off
            target:
              entity_id: switch.v8_jacuzzi_spa_filter
          - delay: "00:00:02"
          - service: switch.turn_off
            target:
              entity_id: switch.v8_jacuzzi_spa_filter
          - delay: "00:00:02"
          - service: switch.turn_off
            target:
              entity_id: switch.v8_jacuzzi_spa_filter
          - delay: "00:00:01"
          - service: input_boolean.turn_off
            target:
              entity_id: input_boolean.v8_jacuzzi_filter_state
          - delay: "00:00:03"
          - if:
              - condition: state
                entity_id: input_boolean.jacuzzi_spa_started_by_pv
                state: "on"
            then:
              - service: switch.turn_off
                target:
                  entity_id: switch.v8_jacuzzi_spa_power
              - service: input_boolean.turn_off
                target:
                  entity_id: input_boolean.jacuzzi_spa_started_by_pv
          - service: system_log.write
            data:
              message: >
                Jacuzzi: AUS – PV-Überschuss unter Schwelle
                ({{ states('sensor.em_available_for_heatpump') }} kW)
              level: info
          - data:
              title: "Jacuzzi gestoppt"
              message: >
                Jacuzzi automatisch gestoppt
                (PV-Überschuss zu gering: {{ states('sensor.em_available_for_heatpump') }} kW)
            action: notify.kottlowski_gmail_com
    
      - alias: "Jacuzzi: Filter Zyklus"
        description: "Startet Filterung zu konfigurierbaren Zeiten – Spa wird bei Bedarf kurz eingeschaltet"
        mode: single
        trigger:
          - platform: time
            at: input_datetime.jacuzzi_filter_time_1
          - platform: time
            at: input_datetime.jacuzzi_filter_time_2
        condition:
          - condition: state
            entity_id: input_boolean.jacuzzi_filter_auto
            state: "on"
          - condition: state
            entity_id: binary_sensor.v8_jacuzzi_spa_connected
            state: "on"
          - condition: state
            entity_id: binary_sensor.v8_jacuzzi_spa_errors
            state: "off"
          - condition: state
            entity_id: input_boolean.v8_jacuzzi_filter_state
            state: "off"
        action:
          - variables:
              spa_was_off: "{{ is_state('switch.v8_jacuzzi_spa_power', 'off') }}"
          - if:
              - condition: template
                value_template: "{{ spa_was_off }}"
            then:
              - service: switch.turn_on
                target:
                  entity_id: switch.v8_jacuzzi_spa_power
              - delay: "00:00:05"
          - service: switch.turn_on
            target:
              entity_id: switch.v8_jacuzzi_spa_filter
          - service: input_boolean.turn_on
            target:
              entity_id: input_boolean.v8_jacuzzi_filter_state
          - service: system_log.write
            data:
              message: >
                Jacuzzi: Filter gestartet für
                {{ states('input_number.jacuzzi_filter_duration') | int }} Min
                (Spa war {{ 'aus – wurde eingeschaltet' if spa_was_off else 'bereits an' }})
              level: info
          - delay:
              minutes: "{{ states('input_number.jacuzzi_filter_duration') | int(60) }}"
          - service: switch.turn_off
            target:
              entity_id: switch.v8_jacuzzi_spa_filter
          - service: input_boolean.turn_off
            target:
              entity_id: input_boolean.v8_jacuzzi_filter_state
          - if:
              - condition: template
                value_template: >
                  {{ spa_was_off and
                     not is_state('climate.v8_jacuzzi_spa_thermostat', 'heat') }}
            then:
              - delay: "00:00:03"
              - service: switch.turn_off
                target:
                  entity_id: switch.v8_jacuzzi_spa_power
          - service: system_log.write
            data:
              message: "Jacuzzi: Filter Zyklus beendet"
              level: info
    
      - alias: "Jacuzzi: Auto-Modus deaktiviert"
        description: "Stoppt den Jacuzzi sofort wenn PV-Auto manuell deaktiviert wird"
        mode: single
        trigger:
          - platform: state
            entity_id: input_boolean.jacuzzi_pv_auto
            to: "off"
        condition:
          - condition: state
            entity_id: switch.v8_jacuzzi_spa_power
            state: "on"
        action:
          - service: climate.set_hvac_mode
            target:
              entity_id: climate.v8_jacuzzi_spa_thermostat
            data:
              hvac_mode: "off"
          - delay: "00:00:02"
          - service: switch.turn_off
            target:
              entity_id: switch.v8_jacuzzi_spa_filter
          - delay: "00:00:02"
          - service: switch.turn_off
            target:
              entity_id: switch.v8_jacuzzi_spa_filter
          - delay: "00:00:02"
          - service: switch.turn_off
            target:
              entity_id: switch.v8_jacuzzi_spa_filter
          - delay: "00:00:01"
          - service: input_boolean.turn_off
            target:
              entity_id: input_boolean.v8_jacuzzi_filter_state
          - delay: "00:00:03"
          - if:
              - condition: state
                entity_id: input_boolean.jacuzzi_spa_started_by_pv
                state: "on"
            then:
              - service: switch.turn_off
                target:
                  entity_id: switch.v8_jacuzzi_spa_power
              - service: input_boolean.turn_off
                target:
                  entity_id: input_boolean.jacuzzi_spa_started_by_pv
          - service: input_boolean.turn_off
            target:
              entity_id: input_boolean.jacuzzi_temp_reached
          - service: system_log.write
            data:
              message: "Jacuzzi: PV-Auto deaktiviert – Heizung und Filter gestoppt"
              level: info
          - data:
              title: "Jacuzzi deaktiviert"
              message: "Jacuzzi PV-Auto wurde deaktiviert. Heizung und Filter wurden gestoppt."
            action: notify.kottlowski_gmail_com
    
      - alias: "Jacuzzi: Zieltemperatur aktualisieren"
        description: "Übergibt geänderte Zieltemperatur sofort an den Thermostat wenn der Spa läuft"
        mode: single
        trigger:
          - platform: state
            entity_id: input_number.jacuzzi_target_temp
        condition:
          - condition: state
            entity_id: switch.v8_jacuzzi_spa_power
            state: "on"
        action:
          - service: climate.set_temperature
            target:
              entity_id: climate.v8_jacuzzi_spa_thermostat
            data:
              temperature: "{{ states('input_number.jacuzzi_target_temp') | float(37) }}"
          - service: system_log.write
            data:
              message: >
                Jacuzzi: Zieltemperatur aktualisiert auf
                {{ states('input_number.jacuzzi_target_temp') }}°C
              level: info
    
      - alias: "Jacuzzi: Fehler-Benachrichtigung"
        description: "Benachrichtigt bei Jacuzzi-Fehler und stoppt falls aktiv"
        mode: single
        trigger:
          - platform: state
            entity_id: binary_sensor.v8_jacuzzi_spa_errors
            to: "on"
        action:
          - data:
              title: "Jacuzzi Fehler!"
              message: "Der Jacuzzi meldet einen Fehler. Bitte überprüfen!"
            action: notify.kottlowski_gmail_com
          - service: system_log.write
            data:
              message: "Jacuzzi: Fehler erkannt – manuelle Prüfung erforderlich"
              level: error
    
      - alias: "Jacuzzi: Zieltemperatur erreicht"
        description: "Schaltet alles ab wenn Zieltemperatur erreicht ist – Neustart nach 2°C Abfall"
        mode: single
        trigger:
          - platform: template
            value_template: >
              {{ state_attr('climate.v8_jacuzzi_spa_thermostat', 'current_temperature') | float(0)
                 >= states('input_number.jacuzzi_target_temp') | float(100) }}
            for: "00:01:00"
        condition:
          - condition: state
            entity_id: input_boolean.jacuzzi_pv_auto
            state: "on"
          - condition: state
            entity_id: switch.v8_jacuzzi_spa_power
            state: "on"
        action:
          - service: climate.set_hvac_mode
            target:
              entity_id: climate.v8_jacuzzi_spa_thermostat
            data:
              hvac_mode: "off"
          - delay: "00:00:02"
          - service: switch.turn_off
            target:
              entity_id: switch.v8_jacuzzi_spa_filter
          - delay: "00:00:02"
          - service: switch.turn_off
            target:
              entity_id: switch.v8_jacuzzi_spa_filter
          - delay: "00:00:02"
          - service: switch.turn_off
            target:
              entity_id: switch.v8_jacuzzi_spa_filter
          - delay: "00:00:01"
          - service: input_boolean.turn_off
            target:
              entity_id: input_boolean.v8_jacuzzi_filter_state
          - delay: "00:00:03"
          - service: switch.turn_off
            target:
              entity_id: switch.v8_jacuzzi_spa_power
          - service: input_boolean.turn_off
            target:
              entity_id: input_boolean.jacuzzi_spa_started_by_pv
          - service: input_boolean.turn_on
            target:
              entity_id: input_boolean.jacuzzi_temp_reached
          - service: system_log.write
            data:
              message: >
                Jacuzzi: Zieltemperatur erreicht – alles ausgeschaltet
                ({{ state_attr('climate.v8_jacuzzi_spa_thermostat', 'current_temperature') }}°C /
                Ziel: {{ states('input_number.jacuzzi_target_temp') }}°C).
                Neustart wenn unter {{ (states('input_number.jacuzzi_target_temp') | float(37) - 2) | round(1) }}°C und PV ausreicht.
              level: info
          - data:
              title: "Jacuzzi: Zieltemperatur erreicht"
              message: >
                Jacuzzi ausgeschaltet – Zieltemperatur
                {{ states('input_number.jacuzzi_target_temp') }}°C erreicht.
                Neustart automatisch bei {{ (states('input_number.jacuzzi_target_temp') | float(37) - 2) | round(1) }}°C.
            action: notify.kottlowski_gmail_com
    
      - alias: "Jacuzzi: Temperatur gesunken – neu starten"
        description: "Startet Jacuzzi neu wenn Temperatur 2°C unter Zieltemperatur und PV-Überschuss ausreicht"
        mode: single
        trigger:
          - platform: template
            value_template: >
              {{ state_attr('climate.v8_jacuzzi_spa_thermostat', 'current_temperature') | float(100)
                 < (states('input_number.jacuzzi_target_temp') | float(37) - 2) }}
            for: "00:01:00"
        condition:
          - condition: state
            entity_id: input_boolean.jacuzzi_pv_auto
            state: "on"
          - condition: state
            entity_id: input_boolean.jacuzzi_temp_reached
            state: "on"
          - condition: state
            entity_id: binary_sensor.v8_jacuzzi_spa_connected
            state: "on"
          - condition: state
            entity_id: binary_sensor.v8_jacuzzi_spa_errors
            state: "off"
          - condition: numeric_state
            entity_id: sensor.em_available_for_heatpump
            above: input_number.jacuzzi_pv_start
        action:
          - service: input_boolean.turn_off
            target:
              entity_id: input_boolean.jacuzzi_temp_reached
          - service: switch.turn_on
            target:
              entity_id: switch.v8_jacuzzi_spa_power
          - service: input_boolean.turn_on
            target:
              entity_id: input_boolean.jacuzzi_spa_started_by_pv
          - delay: "00:00:05"
          - service: climate.set_temperature
            target:
              entity_id: climate.v8_jacuzzi_spa_thermostat
            data:
              temperature: "{{ states('input_number.jacuzzi_target_temp') | float(37) }}"
          - service: climate.set_hvac_mode
            target:
              entity_id: climate.v8_jacuzzi_spa_thermostat
            data:
              hvac_mode: heat
          - service: switch.turn_on
            target:
              entity_id: switch.v8_jacuzzi_spa_filter
          - service: input_boolean.turn_on
            target:
              entity_id: input_boolean.v8_jacuzzi_filter_state
          - service: system_log.write
            data:
              message: >
                Jacuzzi: Temperatur auf
                {{ state_attr('climate.v8_jacuzzi_spa_thermostat', 'current_temperature') }}°C gesunken –
                Neustart (Ziel: {{ states('input_number.jacuzzi_target_temp') }}°C,
                PV: {{ states('sensor.em_available_for_heatpump') }} kW)
              level: info
          - data:
              title: "Jacuzzi: Neustart nach Abkühlung"
              message: >
                Jacuzzi neu gestartet – Temperatur auf
                {{ state_attr('climate.v8_jacuzzi_spa_thermostat', 'current_temperature') }}°C gesunken
                (PV-Überschuss: {{ states('sensor.em_available_for_heatpump') }} kW).
            action: notify.kottlowski_gmail_com
    
    # --- Laufzeit-Protokollierung ---
    sensor:
      - platform: history_stats
        name: "Jacuzzi Heizung Laufzeit heute"
        entity_id: climate.v8_jacuzzi_spa_thermostat
        state: "heat"
        type: time
        start: "{{ now().replace(hour=0, minute=0, second=0) }}"
        end: "{{ now() }}"
    
    template:
      - sensor:
          - name: "Jacuzzi Heizung Laufzeit heute formatiert"
            icon: mdi:timer-play
            state: >
              {% set h = states('sensor.jacuzzi_heizung_laufzeit_heute') | float(0) %}
              {% set hours = h | int %}
              {% set minutes = ((h - hours) * 60) | int %}
              {{ '%d:%02d h' | format(hours, minutes) }}
    

    Status-Button (Lovelace YAML)

    type: custom:button-card
    name: Jacuzzi Status
    icon: mdi:information-outline
    show_label: true
    show_state: false
    tap_action:
      action: none
    hold_action:
      action: more-info
      entity: climate.v8_jacuzzi_spa_thermostat
    triggers_update:
      - climate.v8_jacuzzi_spa_thermostat
      - switch.v8_jacuzzi_spa_filter_ui
      - switch.v8_jacuzzi_spa_jets
      - select.v8_jacuzzi_spa_bubbles
    label: |
      [[[
        var heat    = states['climate.v8_jacuzzi_spa_thermostat'].state;
        var filter  = states['switch.v8_jacuzzi_spa_filter_ui'].state;
        var jets    = states['switch.v8_jacuzzi_spa_jets'].state;
        var bubbles = states['select.v8_jacuzzi_spa_bubbles'].state;
        var parts = [];
        if (heat === 'heat')   parts.push('Heizung AN');
        if (heat === 'idle')   parts.push('Heizung Idle');
        if (filter === 'on')   parts.push('Filter AN');
        if (jets === 'on')     parts.push('Massage AN');
        if (bubbles !== 'OFF') parts.push('Bubbles AN');
        return parts.length > 0 ? parts.join('  ·  ') : 'Alles aus';
      ]]]
    styles:
      card:
        - height: 60px
        - padding: 6px 10px
        - border-radius: 10px
        - cursor: default
        - background: |
            [[[
              var heat    = states['climate.v8_jacuzzi_spa_thermostat'].state;
              var filter  = states['switch.v8_jacuzzi_spa_filter_ui'].state;
              var jets    = states['switch.v8_jacuzzi_spa_jets'].state;
              var bubbles = states['select.v8_jacuzzi_spa_bubbles'].state;
              var anyOn = heat === 'heat' || heat === 'idle' || filter === 'on' || jets === 'on' || bubbles !== 'OFF';
              return anyOn ? '#1565c0' : '#3a3a3a';
            ]]]
        - border: |
            [[[
              var heat    = states['climate.v8_jacuzzi_spa_thermostat'].state;
              var filter  = states['switch.v8_jacuzzi_spa_filter_ui'].state;
              var jets    = states['switch.v8_jacuzzi_spa_jets'].state;
              var bubbles = states['select.v8_jacuzzi_spa_bubbles'].state;
              var anyOn = heat === 'heat' || heat === 'idle' || filter === 'on' || jets === 'on' || bubbles !== 'OFF';
              return anyOn ? '2px solid #1565c0' : '1px solid #666';
            ]]]
      grid:
        - grid-template-areas: "\"i n\" \"i l\""
        - grid-template-columns: 32px auto
        - grid-template-rows: auto auto
        - align-items: center
        - justify-items: start
        - column-gap: 8px
      icon:
        - width: 22px
        - height: 22px
        - color: |
            [[[
              var heat    = states['climate.v8_jacuzzi_spa_thermostat'].state;
              var filter  = states['switch.v8_jacuzzi_spa_filter_ui'].state;
              var jets    = states['switch.v8_jacuzzi_spa_jets'].state;
              var bubbles = states['select.v8_jacuzzi_spa_bubbles'].state;
              var anyOn = heat === 'heat' || heat === 'idle' || filter === 'on' || jets === 'on' || bubbles !== 'OFF';
              return anyOn ? '#ffffff' : '#9e9e9e';
            ]]]
      name:
        - font-size: 14px
        - font-weight: 600
        - justify-self: start
        - text-align: left
        - color: |
            [[[
              var heat    = states['climate.v8_jacuzzi_spa_thermostat'].state;
              var filter  = states['switch.v8_jacuzzi_spa_filter_ui'].state;
              var jets    = states['switch.v8_jacuzzi_spa_jets'].state;
              var bubbles = states['select.v8_jacuzzi_spa_bubbles'].state;
              var anyOn = heat === 'heat' || heat === 'idle' || filter === 'on' || jets === 'on' || bubbles !== 'OFF';
              return anyOn ? '#ffffff' : '#cccccc';
            ]]]
      label:
        - font-size: 12px
        - justify-self: start
        - text-align: left
        - color: |
            [[[
              var heat    = states['climate.v8_jacuzzi_spa_thermostat'].state;
              var filter  = states['switch.v8_jacuzzi_spa_filter_ui'].state;
              var jets    = states['switch.v8_jacuzzi_spa_jets'].state;
              var bubbles = states['select.v8_jacuzzi_spa_bubbles'].state;
              var anyOn = heat === 'heat' || heat === 'idle' || filter === 'on' || jets === 'on' || bubbles !== 'OFF';
              return anyOn ? '#e3f2fd' : '#aaaaaa';
            ]]]
    

    Steuer-Buttons (Lovelace YAML)

    type: vertical-stack
    cards:
      - type: custom:button-card
        entity: switch.v8_jacuzzi_spa_power
        name: SPA Power
        icon: mdi:hot-tub
        show_state: true
        size: 22px
        tap_action:
          action: toggle
        hold_action:
          action: more-info
        state:
          - value: "on"
            styles:
              card:
                - background: "#2e7d32"
                - border: 2px solid "#2e7d32"
              icon:
                - color: "#ffffff"
              name:
                - color: "#ffffff"
              state:
                - color: "#e8f5e9"
          - value: "off"
            styles:
              card:
                - background: "#3a3a3a"
                - border: "1px solid #666"
              icon:
                - color: "#9e9e9e"
              name:
                - color: "#cccccc"
              state:
                - color: "#aaaaaa"
        styles:
          card:
            - height: 60px
            - padding: 6px 10px
            - border-radius: 10px
            - background: var(--ha-card-background)
            - border: 1px solid var(--divider-color)
            - cursor: pointer
          grid:
            - grid-template-areas: "\"i n\" \"i s\""
            - grid-template-columns: 32px auto
            - grid-template-rows: auto auto
            - align-items: center
            - justify-items: start
          icon:
            - width: 22px
            - height: 22px
            - color: var(--secondary-text-color)
          name:
            - font-size: 14px
            - font-weight: 600
            - justify-self: start
            - text-align: left
          state:
            - font-size: 12px
            - opacity: 0.8
            - justify-self: start
            - text-align: left
      - type: custom:button-card
        entity: climate.v8_jacuzzi_spa_thermostat
        name: Heizung
        icon: mdi:fire
        show_state: true
        tap_action:
          action: call-service
          service: script.jacuzzi_heat_toggle
        hold_action:
          action: more-info
        state:
          - value: heat
            styles:
              card:
                - background: "#d32f2f"
                - border: 2px solid "#d32f2f"
              icon:
                - color: "#ffffff"
              name:
                - color: "#ffffff"
              state:
                - color: "#ffebee"
          - value: idle
            styles:
              card:
                - background: "#f57c00"
                - border: 2px solid "#f57c00"
              icon:
                - color: "#ffffff"
              name:
                - color: "#ffffff"
              state:
                - color: "#fff3e0"
          - value: "off"
            styles:
              card:
                - background: "#3a3a3a"
                - border: "1px solid #666"
              icon:
                - color: "#9e9e9e"
              name:
                - color: "#cccccc"
              state:
                - color: "#aaaaaa"
        styles:
          card:
            - height: 60px
            - padding: 6px 10px
            - border-radius: 10px
            - cursor: pointer
          grid:
            - grid-template-areas: "\"i n\" \"i s\""
            - grid-template-columns: 32px auto
            - grid-template-rows: auto auto
            - align-items: center
            - justify-items: start
            - column-gap: 8px
          icon:
            - width: 22px
            - height: 22px
          name:
            - font-size: 14px
            - font-weight: 600
            - justify-self: start
            - text-align: left
          state:
            - font-size: 12px
            - opacity: 0.8
            - justify-self: start
            - text-align: left
      - type: custom:button-card
        entity: switch.v8_jacuzzi_spa_filter_ui
        name: Filterpumpe
        icon: mdi:water-pump
        show_state: true
        size: 22px
        tap_action:
          action: call-service
          service: >
            [[[ return entity.state === 'on' ? 'switch.turn_off' : 'switch.turn_on'
            ]]]
          target:
            entity_id: switch.v8_jacuzzi_spa_filter_ui
        hold_action:
          action: more-info
        state:
          - value: "on"
            styles:
              card:
                - background: "#0288d1"
                - border: 2px solid "#0288d1"
              icon:
                - color: "#ffffff"
              name:
                - color: "#ffffff"
              state:
                - color: "#e1f5fe"
          - value: "off"
            styles:
              card:
                - background: "#3a3a3a"
                - border: "1px solid #666"
              icon:
                - color: "#9e9e9e"
              name:
                - color: "#cccccc"
              state:
                - color: "#aaaaaa"
        styles:
          card:
            - height: 60px
            - padding: 6px 10px
            - border-radius: 10px
            - border: 1px solid var(--divider-color)
            - cursor: pointer
          grid:
            - grid-template-areas: "\"i n\" \"i s\""
            - grid-template-columns: 32px auto
            - grid-template-rows: auto auto
            - align-items: center
            - justify-items: start
            - column-gap: 8px
          icon:
            - width: 22px
            - height: 22px
          name:
            - font-size: 14px
            - font-weight: 600
            - justify-self: start
            - text-align: left
          state:
            - font-size: 12px
            - opacity: 0.8
            - justify-self: start
            - text-align: left
      - type: custom:button-card
        entity: select.v8_jacuzzi_spa_bubbles
        name: Bubble-Modus
        icon: mdi:chart-bubble
        show_state: true
        tap_action:
          action: call-service
          service: select.select_option
          service_data:
            entity_id: select.v8_jacuzzi_spa_bubbles
            option: "[[[ return entity.state === 'MAX' ? 'OFF' : 'MAX'; ]]]"
        hold_action:
          action: more-info
        state:
          - value: "OFF"
            styles:
              card:
                - background: "#3a3a3a"
                - border: "1px solid #666"
              icon:
                - color: "#9e9e9e"
              name:
                - color: "#cccccc"
              state:
                - color: "#aaaaaa"
          - value: MAX
            styles:
              card:
                - background: "#ff9800"
                - border: 2px solid "#ff9800"
              icon:
                - color: "#ffffff"
              name:
                - color: "#ffffff"
              state:
                - color: "#fff8e1"
        styles:
          card:
            - height: 60px
            - padding: 6px 10px
            - border-radius: 10px
          grid:
            - grid-template-areas: "\"i n\" \"i s\""
            - grid-template-columns: 32px auto
            - grid-template-rows: auto auto
            - align-items: center
            - justify-items: start
            - column-gap: 8px
          icon:
            - width: 22px
            - height: 22px
          name:
            - font-size: 14px
            - font-weight: 600
            - justify-self: start
            - text-align: left
          state:
            - font-size: 12px
            - justify-self: start
            - text-align: left
            - opacity: 0.8
      - type: custom:button-card
        entity: switch.v8_jacuzzi_spa_jets
        name: Massagefunktion
        icon: mdi:waves
        show_state: true
        tap_action:
          action: toggle
        hold_action:
          action: more-info
        state:
          - value: "on"
            styles:
              card:
                - background: "#8e24aa"
                - border: 2px solid "#8e24aa"
              icon:
                - color: "#ffffff"
              name:
                - color: "#ffffff"
              state:
                - color: "#f3e5f5"
          - value: "off"
            styles:
              card:
                - background: "#3a3a3a"
                - border: "1px solid #666"
              icon:
                - color: "#9e9e9e"
              name:
                - color: "#cccccc"
              state:
                - color: "#aaaaaa"
        styles:
          card:
            - height: 60px
            - padding: 6px 10px
            - border-radius: 10px
          grid:
            - grid-template-areas: "\"i n\" \"i s\""
            - grid-template-columns: 32px auto
            - grid-template-rows: auto auto
            - align-items: center
            - justify-items: start
            - column-gap: 8px
          icon:
            - width: 22px
            - height: 22px
          name:
            - font-size: 14px
            - font-weight: 600
            - justify-self: start
            - text-align: left
          state:
            - font-size: 12px
            - justify-self: start
            - text-align: left
    grid_options:
      rows: auto
      columns: 6
    card_mod:
      style: |
        ha-card {
          gap: 6px;   /* 🔥 Abstand zwischen Buttons */
        }
    

    Script & Template-Switch (script.yaml / sensors/jacuzzi.yaml)

    In: „/homeassistant/scripts.yaml“

    jacuzzi_heat_toggle:
      alias: "Jacuzzi: Heizung + Filter-UI Toggle (force off with retries)"
      mode: single
      sequence:
        - choose:
            - conditions:
                - condition: state
                  entity_id: climate.v8_jacuzzi_spa_thermostat
                  state: 'off'
              sequence:
                - service: climate.set_hvac_mode
                  target:
                    entity_id: climate.v8_jacuzzi_spa_thermostat
                  data:
                    hvac_mode: 'heat'
                - delay: { seconds: 2 }
                - service: switch.turn_on
                  target:
                    entity_id: switch.v8_jacuzzi_spa_filter
                - service: input_boolean.turn_on
                  target:
                    entity_id: input_boolean.v8_jacuzzi_filter_state
          default:
            - service: climate.turn_off
              target:
                entity_id: climate.v8_jacuzzi_spa_thermostat
            - delay: { seconds: 2 }
            # drei Versuche, den Filter auszuschalten (mit kurzen Pausen)
            - service: switch.turn_off
              target:
                entity_id: switch.v8_jacuzzi_spa_filter
            - delay: { seconds: 2 }
            - service: switch.turn_off
              target:
                entity_id: switch.v8_jacuzzi_spa_filter
            - delay: { seconds: 2 }
            - service: switch.turn_off
              target:
                entity_id: switch.v8_jacuzzi_spa_filter
            - delay: { seconds: 1 }
            - service: input_boolean.turn_off
              target:
                entity_id: input_boolean.v8_jacuzzi_filter_state

    Unter: „/homeassistant/sensors/jacuzzi.yaml“

    - switch:
          - name: "v8-Jacuzzi Spa Filter (UI)"
            unique_id: v8_jacuzzi_filter_ui
            optimistic: false  # optional
            state: "{{ is_state('input_boolean.v8_jacuzzi_filter_state','on') }}"
            turn_on:
              - service: switch.turn_on
                target:
                  entity_id: switch.v8_jacuzzi_spa_filter
              - service: input_boolean.turn_on
                target:
                  entity_id: input_boolean.v8_jacuzzi_filter_state
            turn_off:
              - service: switch.turn_off
                target:
                  entity_id: switch.v8_jacuzzi_spa_filter
              - service: input_boolean.turn_off
                target:
                  entity_id: input_boolean.v8_jacuzzi_filter_state

    Vollständiges Dashboard (Lovelace Raw-Konfiguration)

    Der vollständige YAML-Code meines Jacuzzi Dashboards mit Nordic Theme:

      - type: sections
        max_columns: 3
        title: Jacuzzi
        path: jacuzzi
        icon: mdi:spa
        sections:
          - type: grid
            cards:
              - type: custom:button-card
                entity: switch.jacuzzi
                name: SPA Master Switch
                icon: mdi:electric-switch
                show_state: true
                size: 22px
                tap_action:
                  action: toggle
                hold_action:
                  action: more-info
                state:
                  - value: "on"
                    styles:
                      card:
                        - background: "#2e7d32"
                        - border: "2px solid #ffffff"
                      icon:
                        - color: "#ffffff"
                      name:
                        - color: "#ffffff"
                      state:
                        - color: "#e8f5e9"
                  - value: "off"
                    styles:
                      card:
                        - background: "#c62828"
                        - border: "2px solid #c62828"
                      icon:
                        - color: "#ffffff"
                      name:
                        - color: "#ffffff"
                      state:
                        - color: "#ffebee"
                styles:
                  card:
                    - height: 60px
                    - padding: 6px 10px
                    - border-radius: 10px
                    - cursor: pointer
                  grid:
                    - grid-template-areas: "\"i n s\""
                    - grid-template-columns: 32px auto auto
                    - grid-template-rows: 1fr
                    - align-items: center
                  icon:
                    - width: 22px
                    - height: 22px
                  name:
                    - font-size: 16px
                    - font-weight: 600
                    - justify-self: start
                    - margin-left: 8px
                  state:
                    - font-size: 18px
                    - justify-self: start
                    - font-weight: 700
                    - margin-left: 10px
                    - text-transform: uppercase
              - type: heading
                icon: ""
                heading_style: title
              - type: thermostat
                entity: climate.v8_jacuzzi_spa_thermostat
                show_current_as_primary: true
                grid_options:
                  columns: 6
                  rows: auto
                name: Jacuzzi
              - type: vertical-stack
                cards:
                  - type: custom:button-card
                    entity: switch.v8_jacuzzi_spa_power
                    name: SPA Power
                    icon: mdi:hot-tub
                    show_state: true
                    size: 22px
                    tap_action:
                      action: toggle
                    hold_action:
                      action: more-info
                    state:
                      - value: "on"
                        styles:
                          card:
                            - background: "#2e7d32"
                            - border: "2px solid #ffffff"
                          icon:
                            - color: "#ffffff"
                          name:
                            - color: "#ffffff"
                          state:
                            - color: "#e8f5e9"
                      - value: "off"
                        styles:
                          card:
                            - background: "#3a3a3a"
                            - border: "1px solid #666"
                          icon:
                            - color: "#9e9e9e"
                          name:
                            - color: "#cccccc"
                          state:
                            - color: "#aaaaaa"
                    styles:
                      card:
                        - height: 60px
                        - padding: 6px 10px
                        - border-radius: 10px
                        - background: var(--ha-card-background)
                        - border: 1px solid var(--divider-color)
                        - cursor: pointer
                      grid:
                        - grid-template-areas: "\"i n\" \"i s\""
                        - grid-template-columns: 32px auto
                        - grid-template-rows: auto auto
                        - align-items: center
                        - justify-items: start
                      icon:
                        - width: 22px
                        - height: 22px
                        - color: var(--secondary-text-color)
                      name:
                        - font-size: 14px
                        - font-weight: 600
                        - justify-self: start
                        - text-align: left
                      state:
                        - font-size: 12px
                        - opacity: 0.8
                        - justify-self: start
                        - text-align: left
                  - type: custom:button-card
                    entity: climate.v8_jacuzzi_spa_thermostat
                    name: Heizung
                    icon: mdi:fire
                    show_state: true
                    tap_action:
                      action: call-service
                      service: script.jacuzzi_heat_toggle
                    hold_action:
                      action: more-info
                    state:
                      - value: heat
                        styles:
                          card:
                            - background: "#d32f2f"
                            - border: 2px solid "#d32f2f"
                          icon:
                            - color: "#ffffff"
                          name:
                            - color: "#ffffff"
                          state:
                            - color: "#ffebee"
                      - value: idle
                        styles:
                          card:
                            - background: "#f57c00"
                            - border: 2px solid "#f57c00"
                          icon:
                            - color: "#ffffff"
                          name:
                            - color: "#ffffff"
                          state:
                            - color: "#fff3e0"
                      - value: "off"
                        styles:
                          card:
                            - background: "#3a3a3a"
                            - border: "1px solid #666"
                          icon:
                            - color: "#9e9e9e"
                          name:
                            - color: "#cccccc"
                          state:
                            - color: "#aaaaaa"
                    styles:
                      card:
                        - height: 60px
                        - padding: 6px 10px
                        - border-radius: 10px
                        - cursor: pointer
                      grid:
                        - grid-template-areas: "\"i n\" \"i s\""
                        - grid-template-columns: 32px auto
                        - grid-template-rows: auto auto
                        - align-items: center
                        - justify-items: start
                        - column-gap: 8px
                      icon:
                        - width: 22px
                        - height: 22px
                      name:
                        - font-size: 14px
                        - font-weight: 600
                        - justify-self: start
                        - text-align: left
                      state:
                        - font-size: 12px
                        - opacity: 0.8
                        - justify-self: start
                        - text-align: left
                  - type: custom:button-card
                    entity: switch.v8_jacuzzi_spa_filter_ui
                    name: Filterpumpe
                    icon: mdi:water-pump
                    show_state: true
                    size: 22px
                    tap_action:
                      action: call-service
                      service: >
                        [[[ return entity.state === 'on' ? 'switch.turn_off' :
                        'switch.turn_on' ]]]
                      target:
                        entity_id: switch.v8_jacuzzi_spa_filter_ui
                    hold_action:
                      action: more-info
                    state:
                      - value: "on"
                        styles:
                          card:
                            - background: "#0288d1"
                            - border: 2px solid "#0288d1"
                          icon:
                            - color: "#ffffff"
                          name:
                            - color: "#ffffff"
                          state:
                            - color: "#e1f5fe"
                      - value: "off"
                        styles:
                          card:
                            - background: "#3a3a3a"
                            - border: "1px solid #666"
                          icon:
                            - color: "#9e9e9e"
                          name:
                            - color: "#cccccc"
                          state:
                            - color: "#aaaaaa"
                    styles:
                      card:
                        - height: 60px
                        - padding: 6px 10px
                        - border-radius: 10px
                        - border: 1px solid var(--divider-color)
                        - cursor: pointer
                      grid:
                        - grid-template-areas: "\"i n\" \"i s\""
                        - grid-template-columns: 32px auto
                        - grid-template-rows: auto auto
                        - align-items: center
                        - justify-items: start
                        - column-gap: 8px
                      icon:
                        - width: 22px
                        - height: 22px
                      name:
                        - font-size: 14px
                        - font-weight: 600
                        - justify-self: start
                        - text-align: left
                      state:
                        - font-size: 12px
                        - opacity: 0.8
                        - justify-self: start
                        - text-align: left
                  - type: custom:button-card
                    entity: select.v8_jacuzzi_spa_bubbles
                    name: Bubble-Modus
                    icon: mdi:chart-bubble
                    show_state: true
                    tap_action:
                      action: call-service
                      service: select.select_option
                      service_data:
                        entity_id: select.v8_jacuzzi_spa_bubbles
                        option: "[[[ return entity.state === 'MAX' ? 'OFF' : 'MAX'; ]]]"
                    hold_action:
                      action: more-info
                    state:
                      - value: "OFF"
                        styles:
                          card:
                            - background: "#3a3a3a"
                            - border: "1px solid #666"
                          icon:
                            - color: "#9e9e9e"
                          name:
                            - color: "#cccccc"
                          state:
                            - color: "#aaaaaa"
                      - value: MAX
                        styles:
                          card:
                            - background: "#ff9800"
                            - border: 2px solid "#ff9800"
                          icon:
                            - color: "#ffffff"
                          name:
                            - color: "#ffffff"
                          state:
                            - color: "#fff8e1"
                    styles:
                      card:
                        - height: 60px
                        - padding: 6px 10px
                        - border-radius: 10px
                      grid:
                        - grid-template-areas: "\"i n\" \"i s\""
                        - grid-template-columns: 32px auto
                        - grid-template-rows: auto auto
                        - align-items: center
                        - justify-items: start
                        - column-gap: 8px
                      icon:
                        - width: 22px
                        - height: 22px
                      name:
                        - font-size: 14px
                        - font-weight: 600
                        - justify-self: start
                        - text-align: left
                      state:
                        - font-size: 12px
                        - justify-self: start
                        - text-align: left
                        - opacity: 0.8
                  - type: custom:button-card
                    entity: switch.v8_jacuzzi_spa_jets
                    name: Massagefunktion
                    icon: mdi:waves
                    show_state: true
                    tap_action:
                      action: toggle
                    hold_action:
                      action: more-info
                    state:
                      - value: "on"
                        styles:
                          card:
                            - background: "#8e24aa"
                            - border: 2px solid "#8e24aa"
                          icon:
                            - color: "#ffffff"
                          name:
                            - color: "#ffffff"
                          state:
                            - color: "#f3e5f5"
                      - value: "off"
                        styles:
                          card:
                            - background: "#3a3a3a"
                            - border: "1px solid #666"
                          icon:
                            - color: "#9e9e9e"
                          name:
                            - color: "#cccccc"
                          state:
                            - color: "#aaaaaa"
                    styles:
                      card:
                        - height: 60px
                        - padding: 6px 10px
                        - border-radius: 10px
                      grid:
                        - grid-template-areas: "\"i n\" \"i s\""
                        - grid-template-columns: 32px auto
                        - grid-template-rows: auto auto
                        - align-items: center
                        - justify-items: start
                        - column-gap: 8px
                      icon:
                        - width: 22px
                        - height: 22px
                      name:
                        - font-size: 14px
                        - font-weight: 600
                        - justify-self: start
                        - text-align: left
                      state:
                        - font-size: 12px
                        - justify-self: start
                        - text-align: left
                grid_options:
                  rows: auto
                  columns: 6
                card_mod:
                  style: |
                    ha-card {
                      gap: 6px;   /* 🔥 Abstand zwischen Buttons */
                    }
          - type: grid
            cards:
              - type: gauge
                name: Jacuzzi
                severity:
                  green: 0
                  yellow: 800
                  red: 1500
                needle: true
                entity: sensor.jacuzzi_leistung
                max: 2300
              - type: custom:apexcharts-card
                header:
                  title: Jacuzzi Energie (14 Tage)
                  show: true
                graph_span: 14d
                span:
                  start: day
                  offset: "-13d"
                series:
                  - entity: sensor.jacuzzi_energy_daily
                    name: Jacuzzi
                    type: column
                    group_by:
                      func: max
                      duration: 1d
                    transform: |
                      return x < 0 ? 0 : x;
                apex_config:
                  chart:
                    height: 300
                  plotOptions:
                    bar:
                      borderRadius: 4
                      columnWidth: 60%
                      colors:
                        ranges:
                          - from: 0
                            to: 0.5
                            color: "#90EE90"
                          - from: 0.5
                            to: 0.7
                            color: "#00C853"
                          - from: 0.7
                            to: 15
                            color: "#FFD600"
                  xaxis:
                    labels:
                      datetimeFormatter:
                        day: dd.MM
                  yaxis:
                    min: 0
              - type: custom:apexcharts-card
                header:
                  title: Jacuzzi Energie (stündlich)
                  show: true
                graph_span: 24h
                series:
                  - entity: sensor.jacuzzi_energie
                    name: Solar
                    type: column
                    group_by:
                      func: diff
                      duration: 1h
                    transform: |
                      return x < 0 ? 0 : x;
                apex_config:
                  chart:
                    height: 300
                  plotOptions:
                    bar:
                      borderRadius: 4
                      columnWidth: 60%
                      colors:
                        ranges:
                          - from: 0
                            to: 4
                            color: "#90EE90"
                          - from: 4
                            to: 8
                            color: "#00C853"
                          - from: 8
                            to: 15
                            color: "#FFD600"
                  xaxis:
                    labels:
                      datetimeFormatter:
                        hour: HH:mm
                  yaxis:
                    min: 0
                  tooltip:
                    "y":
                      formatter: |
                        function(value) {
                          return Math.round(value) + " W";
                        }
          - type: grid
            cards:
              - type: heading
                heading_style: title
                icon: mdi:shower-head
                heading: Dusche
              - square: false
                type: grid
                cards:
                  - type: custom:button-card
                    entity: valve.morpheus_reich_valve_4
                    name: Dusche
                    icon: mdi:shower-head
                    show_state: true
                    size: 22px
                    state_display: |
                      [[[
                        return entity.state === 'open' ? 'Ein' : 'Aus';
                      ]]]
                    tap_action:
                      action: toggle
                    hold_action:
                      action: more-info
                    state:
                      - value: open
                        styles:
                          card:
                            - background: "#2e7d32"
                            - border: 2px solid "#2e7d32"
                          icon:
                            - color: "#ffffff"
                          name:
                            - color: "#ffffff"
                          state:
                            - color: "#e8f5e9"
                      - value: closed
                        styles:
                          card:
                            - background: "#3a3a3a"
                            - border: "1px solid #666"
                          icon:
                            - color: "#9e9e9e"
                          name:
                            - color: "#cccccc"
                          state:
                            - color: "#aaaaaa"
                    styles:
                      card:
                        - height: 60px
                        - padding: 6px 10px
                        - border-radius: 10px
                        - background: var(--ha-card-background)
                        - border: 1px solid var(--divider-color)
                        - cursor: pointer
                      grid:
                        - grid-template-areas: "\"i n\" \"i s\""
                        - grid-template-columns: 32px auto
                        - grid-template-rows: auto auto
                        - align-items: center
                        - justify-items: start
                      icon:
                        - width: 22px
                        - height: 22px
                        - color: var(--secondary-text-color)
                      name:
                        - font-size: 14px
                        - font-weight: 600
                        - justify-self: start
                        - text-align: left
                      state:
                        - font-size: 12px
                        - opacity: 0.8
                        - justify-self: start
                        - text-align: left
                columns: 2
              - square: false
                type: grid
                cards:
                  - type: custom:button-card
                    name: Dauer
                    icon: mdi:timer
                    show_name: false
                    entity: input_number.dusche_laufzeit
                    tap_action:
                      action: call-service
                      service: input_number.increment
                      target:
                        entity_id: input_number.dusche_laufzeit
                    hold_action:
                      action: call-service
                      service: input_number.decrement
                      target:
                        entity_id: input_number.dusche_laufzeit
                    double_tap_action:
                      action: more-info
                    show_state: true
                    state_display: |
                      [[[
                        const v = Number(entity.state);
                        return `${v} min`;
                      ]]]
                    aspect_ratio: 3/1
                    size: 22px
                    styles:
                      card:
                        - height: 60px
                        - padding: 6px 10px
                        - border-radius: 10px
                        - border: 1px solid rgba(255,255,255,0.2)
                        - background: |
                            [[[
                              const v = Number(entity.state);
                              if (v === 0) return '#424242';
                              if (v <= 90) return '#1565c0';
                              if (v <= 150) return '#f9a825';
                              return '#c62828';
                            ]]]
                      name:
                        - font-size: 14px
                        - font-weight: 600
                        - color: "#90caf9"
                      icon:
                        - width: 22px
                        - height: 22px
                        - color: "#90caf9"
                      state:
                        - font-size: 14px
                        - font-weight: bold
                        - color: "#ffffff"
                columns: 2
              - type: heading
                heading: Jacuzzi Filterung
                heading_style: title
                icon: mdi:filter-outline
              - square: false
                type: grid
                cards:
                  - type: custom:button-card
                    entity: input_boolean.jacuzzi_filter_auto
                    name: Filter-Automatik
                    icon: mdi:filter
                    show_state: true
                    size: 22px
                    tap_action:
                      action: toggle
                    hold_action:
                      action: more-info
                    state:
                      - value: "on"
                        styles:
                          card:
                            - background: "#2e7d32"
                            - border: "2px solid #ffffff"
                          icon:
                            - color: "#ffffff"
                          name:
                            - color: "#ffffff"
                          state:
                            - color: "#e8f5e9"
                      - value: "off"
                        styles:
                          card:
                            - background: "#3a3a3a"
                            - border: "1px solid #666"
                          icon:
                            - color: "#9e9e9e"
                          name:
                            - color: "#cccccc"
                          state:
                            - color: "#aaaaaa"
                    styles:
                      card:
                        - height: 60px
                        - padding: 6px 10px
                        - border-radius: 10px
                        - background: var(--ha-card-background)
                        - border: 1px solid var(--divider-color)
                        - cursor: pointer
                      grid:
                        - grid-template-areas: "\"i n\" \"i s\""
                        - grid-template-columns: 32px auto
                        - grid-template-rows: auto auto
                        - align-items: center
                        - justify-items: start
                      icon:
                        - width: 22px
                        - height: 22px
                        - color: var(--secondary-text-color)
                      name:
                        - font-size: 14px
                        - font-weight: 600
                        - justify-self: start
                        - text-align: left
                      state:
                        - font-size: 12px
                        - opacity: 0.8
                        - justify-self: start
                        - text-align: left
                columns: 2
                grid_options:
                  rows: 1
              - square: false
                type: grid
                columns: 3
                grid_options:
                  rows: auto
                cards:
                  - type: custom:button-card
                    entity: input_datetime.jacuzzi_filter_time_1
                    icon: mdi:clock-start
                    show_name: false
                    show_state: true
                    show_icon: true
                    tap_action:
                      action: more-info
                    layout: vertical
                    aspect_ratio: 3/1
                    size: 22px
                    styles:
                      card:
                        - height: 60px
                        - padding: 4px
                        - border-radius: 10px
                        - border: 1px solid rgba(255,255,255,0.2)
                        - background: "#1565c0"
                      grid:
                        - grid-template-areas: "\"i\" \"s\""
                        - grid-template-rows: 1fr auto
                        - justify-items: center
                      icon:
                        - width: 24px
                        - height: 24px
                        - color: "#90caf9"
                      state:
                        - font-size: 14px
                        - font-weight: bold
                        - color: "#ffffff"
                  - type: custom:button-card
                    entity: input_datetime.jacuzzi_filter_time_2
                    icon: mdi:clock-start
                    show_name: false
                    show_state: true
                    show_icon: true
                    tap_action:
                      action: more-info
                    layout: vertical
                    aspect_ratio: 3/1
                    size: 22px
                    styles:
                      card:
                        - height: 60px
                        - padding: 4px
                        - border-radius: 10px
                        - border: 1px solid rgba(255,255,255,0.2)
                        - background: "#1565c0"
                      grid:
                        - grid-template-areas: "\"i\" \"s\""
                        - grid-template-rows: 1fr auto
                        - justify-items: center
                      icon:
                        - width: 24px
                        - height: 24px
                        - color: "#90caf9"
                      state:
                        - font-size: 14px
                        - font-weight: bold
                        - color: "#ffffff"
                  - type: custom:button-card
                    name: Dauer
                    icon: mdi:timer
                    show_name: false
                    entity: input_number.jacuzzi_filter_duration
                    tap_action:
                      action: call-service
                      service: input_number.increment
                      target:
                        entity_id: input_number.jacuzzi_filter_duration
                    hold_action:
                      action: call-service
                      service: input_number.decrement
                      target:
                        entity_id: input_number.jacuzzi_filter_duration
                    double_tap_action:
                      action: more-info
                    show_state: true
                    state_display: |
                      [[[
                        const v = Number(entity.state);
                        return `${v} min`;
                      ]]]
                    aspect_ratio: 3/1
                    size: 22px
                    styles:
                      card:
                        - height: 60px
                        - padding: 6px 10px
                        - border-radius: 10px
                        - border: 1px solid rgba(255,255,255,0.2)
                        - background: |
                            [[[
                              const v = Number(entity.state);
                              if (v === 0) return '#424242';
                              if (v <= 90) return '#1565c0';
                              if (v <= 150) return '#f9a825';
                              return '#c62828';
                            ]]]
                      name:
                        - font-size: 14px
                        - font-weight: 600
                        - color: "#90caf9"
                      icon:
                        - width: 22px
                        - height: 22px
                        - color: "#90caf9"
                      state:
                        - font-size: 14px
                        - font-weight: bold
                        - color: "#ffffff"
              - type: heading
                heading_style: title
              - type: heading
                heading_style: title
                icon: mdi:heating-coil
                heading: Jacuzzi Automatisierung Filter
              - square: false
                type: grid
                columns: 2
                grid_options:
                  rows: 1
                cards:
                  - type: custom:button-card
                    entity: automation.jacuzzi_filter_zyklus
                    name: Jacuzzi-Automatisierungen
                    show_name: true
                    show_icon: false
                    show_state: true
                    layout: vertical
                    tap_action:
                      action: call-service
                      service: automation.toggle
                      target:
                        entity_id:
                          - automation.jacuzzi_filter_zyklus
                    state_display: |
                      [[[
                        return entity.state === 'on' ? 'aktiv' : 'inaktiv';
                      ]]]
                    styles:
                      card:
                        - height: 60px
                        - padding: 6px 10px
                        - border-radius: 10px
                        - border: 1px solid
                      grid:
                        - grid-template-areas: "\"n\" \"s\""
                        - grid-template-rows: auto auto
                        - justify-items: start
                      name:
                        - font-size: 14px
                        - font-weight: 600
                      state:
                        - font-size: 13px
                        - font-weight: bold
                    state:
                      - value: "on"
                        styles:
                          card:
                            - background: "#2e7d32"
                          name:
                            - color: "#e8f5e9"
                          state:
                            - color: "#e8f5e9"
                      - value: "off"
                        styles:
                          card:
                            - background: "#c62828"
                          name:
                            - color: "#ffebee"
                          state:
                            - color: "#ffebee"
              - type: heading
                heading_style: title
              - type: heading
                heading_style: title
                icon: mdi:heating-coil
                heading: Jacuzzi Heizung
              - square: false
                type: grid
                cards:
                  - type: custom:button-card
                    entity: input_boolean.jacuzzi_pv_auto
                    name: Heiz-Automatik
                    icon: mdi:heating-coil
                    show_state: true
                    size: 22px
                    tap_action:
                      action: toggle
                    hold_action:
                      action: more-info
                    state:
                      - value: "on"
                        styles:
                          card:
                            - background: "#2e7d32"
                            - border: "2px solid #ffffff"
                          icon:
                            - color: "#ffffff"
                          name:
                            - color: "#ffffff"
                          state:
                            - color: "#e8f5e9"
                      - value: "off"
                        styles:
                          card:
                            - background: "#3a3a3a"
                            - border: "1px solid #666"
                          icon:
                            - color: "#9e9e9e"
                          name:
                            - color: "#cccccc"
                          state:
                            - color: "#aaaaaa"
                    styles:
                      card:
                        - height: 60px
                        - padding: 6px 10px
                        - border-radius: 10px
                        - background: var(--ha-card-background)
                        - border: 1px solid var(--divider-color)
                        - cursor: pointer
                      grid:
                        - grid-template-areas: "\"i n\" \"i s\""
                        - grid-template-columns: 32px auto
                        - grid-template-rows: auto auto
                        - align-items: center
                        - justify-items: start
                      icon:
                        - width: 22px
                        - height: 22px
                        - color: var(--secondary-text-color)
                      name:
                        - font-size: 14px
                        - font-weight: 600
                        - justify-self: start
                        - text-align: left
                      state:
                        - font-size: 12px
                        - opacity: 0.8
                        - justify-self: start
                        - text-align: left
                columns: 2
                grid_options:
                  rows: 1
              - square: false
                type: grid
                cards:
                  - type: custom:button-card
                    name: Laufzeit Heizung
                    entity: sensor.jacuzzi_heizung_laufzeit_heute
                    icon: mdi:timer-play
                    show_name: true
                    show_icon: true
                    show_state: true
                    tap_action:
                      action: none
                    double_tap_action:
                      action: none
                    hold_action:
                      action: more-info
                    aspect_ratio: 3/1
                    size: 22px
                    state_display: |
                      [[[
                        const h = Number(entity.state);
                        const hours = Math.floor(h);
                        const minutes = Math.round((h - hours) * 60);
                        return `${hours}:${String(minutes).padStart(2,'0')} h`;
                      ]]]
                    styles:
                      card:
                        - height: 60px
                        - padding: 6px 10px
                        - border-radius: 10px
                        - border: 1px solid
                        - background: "#4a148c"
                        - cursor: default
                      grid:
                        - grid-template-areas: "\"i n\" \"i s\""
                        - grid-template-columns: 32px auto
                        - grid-template-rows: auto auto
                        - align-items: center
                        - justify-items: start
                      icon:
                        - width: 22px
                        - height: 22px
                        - color: "#ce93d8"
                      name:
                        - font-size: 14px
                        - font-weight: 600
                        - color: "#ce93d8"
                        - justify-self: start
                        - text-align: left
                      state:
                        - font-size: 14px
                        - font-weight: bold
                        - color: "#ffffff"
                        - justify-self: start
                        - text-align: left
                columns: 2
                grid_options:
                  rows: 1
              - square: false
                type: grid
                cards:
                  - type: custom:button-card
                    name: Dauer
                    icon: mdi:timer
                    show_name: false
                    entity: input_number.jacuzzi_min_runtime
                    tap_action:
                      action: call-service
                      service: input_number.increment
                      target:
                        entity_id: input_number.jacuzzi_min_runtime
                    hold_action:
                      action: call-service
                      service: input_number.decrement
                      target:
                        entity_id: input_number.jacuzzi_min_runtime
                    double_tap_action:
                      action: more-info
                    show_state: true
                    state_display: |
                      [[[
                        const v = Number(entity.state);
                        return `${v} min`;
                      ]]]
                    aspect_ratio: 3/1
                    size: 22px
                    styles:
                      card:
                        - height: 60px
                        - padding: 6px 10px
                        - border-radius: 10px
                        - border: 1px solid rgba(255,255,255,0.2)
                        - background: |
                            [[[
                              const v = Number(entity.state);
                              if (v === 0) return '#424242';
                              if (v <= 90) return '#1565c0';
                              if (v <= 150) return '#f9a825';
                              return '#c62828';
                            ]]]
                      name:
                        - font-size: 14px
                        - font-weight: 600
                        - color: "#90caf9"
                      icon:
                        - width: 22px
                        - height: 22px
                        - color: "#90caf9"
                      state:
                        - font-size: 14px
                        - font-weight: bold
                        - color: "#ffffff"
                  - type: custom:button-card
                    name: Dauer
                    icon: mdi:hydraulic-oil-temperature
                    show_name: false
                    entity: input_number.jacuzzi_target_temp
                    tap_action:
                      action: call-service
                      service: input_number.increment
                      target:
                        entity_id: input_number.jacuzzi_target_temp
                    hold_action:
                      action: call-service
                      service: input_number.decrement
                      target:
                        entity_id: input_number.jacuzzi_target_temp
                    double_tap_action:
                      action: more-info
                    show_state: true
                    state_display: |
                      [[[
                        const v = Number(entity.state);
                        return `${v} Grad`;
                      ]]]
                    aspect_ratio: 3/1
                    size: 22px
                    styles:
                      card:
                        - height: 60px
                        - padding: 6px 10px
                        - border-radius: 10px
                        - border: 1px solid rgba(255,255,255,0.2)
                        - background: |
                            [[[
                              const v = Number(entity.state);
                              if (v === 0) return '#424242';
                              if (v <= 90) return '#1565c0';
                              if (v <= 150) return '#f9a825';
                              return '#c62828';
                            ]]]
                      name:
                        - font-size: 14px
                        - font-weight: 600
                        - color: "#90caf9"
                      icon:
                        - width: 22px
                        - height: 22px
                        - color: "#90caf9"
                      state:
                        - font-size: 14px
                        - font-weight: bold
                        - color: "#ffffff"
                columns: 2
                grid_options:
                  rows: auto
              - type: heading
                heading_style: title
                icon: mdi:heating-coil
                heading: Jacuzzi Automatisierungen Heizung
              - square: false
                type: grid
                columns: 2
                grid_options:
                  rows: auto
                cards:
                  - type: custom:button-card
                    entity: automation.jacuzzi_pv_uberschuss_ein
                    name: PV-Automatisierungen
                    show_name: true
                    show_icon: false
                    show_state: true
                    layout: vertical
                    tap_action:
                      action: call-service
                      service: automation.toggle
                      target:
                        entity_id:
                          - automation.jacuzzi_pv_uberschuss_ein
                          - automation.jacuzzi_pv_uberschuss_aus
                    hold_action:
                      action: more-info
                    state_display: |
                      [[[
                        return entity.state === 'on' ? 'aktiv' : 'inaktiv';
                      ]]]
                    styles:
                      card:
                        - height: 60px
                        - padding: 6px 10px
                        - border-radius: 10px
                        - border: 1px solid
                      grid:
                        - grid-template-areas: "\"n\" \"s\""
                        - grid-template-rows: auto auto
                        - justify-items: start
                      name:
                        - font-size: 14px
                        - font-weight: 600
                      state:
                        - font-size: 13px
                        - font-weight: bold
                    state:
                      - value: "on"
                        styles:
                          card:
                            - background: "#2e7d32"
                          name:
                            - color: "#e8f5e9"
                          state:
                            - color: "#e8f5e9"
                      - value: "off"
                        styles:
                          card:
                            - background: "#c62828"
                          name:
                            - color: "#ffebee"
                          state:
                            - color: "#ffebee"
              - square: false
                type: grid
                cards:
                  - type: custom:button-card
                    name: Aktueller PV-Überschuß
                    icon: mdi:solar-power
                    show_name: true
                    show_icon: true
                    entity: sensor.em_available_for_heatpump
                    tap_action:
                      action: none
                    hold_action:
                      action: more-info
                    double_tap_action:
                      action: none
                    show_state: true
                    state_display: |
                      [[[
                        const v = Number(entity.state);
                        return `${v} kW`;
                      ]]]
                    aspect_ratio: 3/1
                    size: 22px
                    styles:
                      card:
                        - height: 60px
                        - padding: 6px 10px
                        - border-radius: 10px
                        - border: 1px solid
                        - background: "#4a148c"
                        - cursor: default
                      grid:
                        - grid-template-areas: "\"i n\" \"i s\""
                        - grid-template-columns: 32px auto
                        - grid-template-rows: auto auto
                        - align-items: center
                        - justify-items: start
                      icon:
                        - width: 22px
                        - height: 22px
                        - color: "#ce93d8"
                      name:
                        - font-size: 14px
                        - font-weight: 600
                        - color: "#ce93d8"
                        - justify-self: start
                        - text-align: left
                      state:
                        - font-size: 14px
                        - font-weight: bold
                        - color: |
                            [[[
                              const v = Number(entity.state);
                              if (v === 0) return '#f9a825';
                              if (v <= 5) return '#a5d6a7';
                              return '#ffffff';
                            ]]]
                        - justify-self: start
                        - text-align: left
                columns: 2
                grid_options:
                  rows: 1
              - square: false
                type: grid
                cards:
                  - type: custom:button-card
                    name: PV-Überschuß Stop
                    icon: mdi:power
                    show_name: true
                    show_icon: false
                    entity: input_number.jacuzzi_pv_stop
                    tap_action:
                      action: call-service
                      service: input_number.increment
                      target:
                        entity_id: input_number.jacuzzi_pv_stop
                    hold_action:
                      action: call-service
                      service: input_number.decrement
                      target:
                        entity_id: input_number.jacuzzi_pv_stop
                    double_tap_action:
                      action: more-info
                    show_state: true
                    state_display: |
                      [[[
                        const v = Number(entity.state);
                        return `${v} kW`;
                      ]]]
                    aspect_ratio: 3/1
                    size: 22px
                    styles:
                      card:
                        - height: 60px
                        - padding: 6px 10px
                        - border-radius: 10px
                        - border: 1px solid rgba(255,255,255,0.2)
                        - background: |
                            [[[
                              const v = Number(entity.state);
                              if (v === 0) return '#424242';
                              if (v <= 1) return '#1565c0';
                              if (v <= 2) return '#f9a825';
                              return '#c62828';
                            ]]]
                      name:
                        - font-size: 14px
                        - font-weight: 600
                        - color: "#90caf9"
                      icon:
                        - width: 22px
                        - height: 22px
                        - color: "#90caf9"
                      state:
                        - font-size: 14px
                        - font-weight: bold
                        - color: "#ffffff"
                  - type: custom:button-card
                    name: PV-Überschuß Start
                    icon: mdi:power
                    show_name: true
                    show_icon: false
                    entity: input_number.jacuzzi_pv_start
                    tap_action:
                      action: call-service
                      service: input_number.increment
                      target:
                        entity_id: input_number.jacuzzi_pv_start
                    hold_action:
                      action: call-service
                      service: input_number.decrement
                      target:
                        entity_id: input_number.jacuzzi_pv_start
                    double_tap_action:
                      action: more-info
                    show_state: true
                    state_display: |
                      [[[
                        const v = Number(entity.state);
                        return `${v} kW`;
                      ]]]
                    aspect_ratio: 3/1
                    size: 22px
                    styles:
                      card:
                        - height: 60px
                        - padding: 6px 10px
                        - border-radius: 10px
                        - border: 1px solid rgba(255,255,255,0.2)
                        - background: |
                            [[[
                              const v = Number(entity.state);
                              if (v === 0) return '#424242';
                              if (v <= 2) return '#1565c0';
                              if (v <= 3) return '#f9a825';
                              return '#c62828';
                            ]]]
                      name:
                        - font-size: 14px
                        - font-weight: 600
                        - color: "#90caf9"
                      icon:
                        - width: 22px
                        - height: 22px
                        - color: "#90caf9"
                      state:
                        - font-size: 14px
                        - font-weight: bold
                        - color: "#ffffff"
                columns: 2
                grid_options:
                  rows: 1
        visible: []
        cards: []
        header:
          card:
            type: markdown
            text_only: true
            content: "# 🌿 Jacuzzi 🫧"
        subview: true
    

    🚿 Teil 2: Die smarte Gartendusche


    🔧 Ausgangsproblem & Hintergrund

    Meine Gartendusche ist Teil meiner Gardena Gartenbewässerung. Die gesamte Bewässerungsanlage wird über die Gardena Integration in Home Assistant eingebunden – damit lassen sich alle Ventile der Anlage direkt aus HA heraus steuern.

    Die Gartendusche hat dabei ein eigenes dediziertes Ventil in der Bewässerungsanlage, das unabhängig von den anderen Bewässerungskreisen angesteuert werden kann. In Home Assistant erscheint es als valve.morpheus_reich_valve_4 bzw. Dusche nach Umbenennung.

    📸 Screenshot der Gardena Integration in Home Assistant

    Das Problem: Wer kennt das nicht – man dreht die Dusche auf, geht kurz ins Haus und vergisst sie oder man nutzt die Dusche nur kurz und es liegt die ganze Zeit der Wassserdruck an. Gerade bei einer Gartendusche, die über ein steuerbares Ventil läuft, ist eine automatische Abschaltung ein Muss.

    Außerdem liefert das Ventil intern die Zustände „open“ und „closed“ – für ein deutschsprachiges Dashboard möchte ich lieber „Ein“ und „Aus“ sehen.


    🎯 Ziele

    • ⏱️ Dusche schaltet sich nach konfigurierbarer Zeit automatisch ab (Standard: 60 Minuten)
    • 🔢 Laufzeit per Eingabefeld einstellbar (5–180 Minuten) – kein Schieberegler
    • 🔄 Öffnet man die Dusche erneut während der Timer läuft, startet der Countdown von vorne
    • 🏷️ Dashboard-Button zeigt „Ein“ / „Aus“ statt der internen Zustände „open“ / „closed“

    🧩 Helper

    • input_number.dusche_laufzeit – Einstellbare Laufzeit bis zum automatischen Abschalten. Standard: 30 Min, Bereich: 5–180 Min, Eingabe als Zahlenfeld (kein Schieberegler).

    🤖 Die Automation: Auto-Aus nach Laufzeit

    Die Automation ist einfach und zuverlässig aufgebaut:

    • Trigger: Ventil wird geöffnet
    • Action: Warte die konfigurierte Laufzeit, prüfe ob das Ventil noch offen ist, schließe es dann automatisch
    • mode: restart – Wird die Dusche erneut geöffnet während der Timer läuft, startet der Countdown neu. Es läuft immer nur ein Timer gleichzeitig.

    Die Condition vor dem Schließen ist wichtig: Hat jemand die Dusche in der Zwischenzeit manuell geschlossen, passiert beim automatischen Schließversuch nichts – kein unnötiger Schaltvorgang.


    🖥️ Die Dashboard-Button

    Die Button folgt dem gleichen Design wie die Jacuzzi-Buttons: grüner Hintergrund wenn die Dusche läuft, dunkler Hintergrund wenn sie geschlossen ist. Der interne Zustand „open“ / „closed“ wird per state_display-Template in „Ein“ / „Aus“ übersetzt. Und dann noch der Button über den die Laufzeit eingestellt werden kann.

    📸 Screenshot des Dusche-Buttons im Dashboard – sowie der Laufzeit

    📋 Der YAML-Code

    Helper & Automation (config/packages/dusche.yaml)

    # config/packages/dusche.yaml
    
    # --- Helper ---
    input_number:
      dusche_laufzeit:
        name: "Dusche Laufzeit (Min)"
        initial: 60
        min: 5
        max: 180
        step: 5
        unit_of_measurement: min
        mode: box
        icon: mdi:timer-outline
    
    # --- Automation ---
    automation:
      - alias: "Dusche: Auto-Aus nach Laufzeit"
        description: "Schließt das Duschventil automatisch nach konfigurierbarer Zeit"
        mode: restart
        trigger:
          - platform: state
            entity_id: valve.morpheus_reich_valve_4
            to: "open"
        action:
          - delay:
              minutes: "{{ states('input_number.dusche_laufzeit') | int(60) }}"
          - condition: state
            entity_id: valve.morpheus_reich_valve_4
            state: "open"
          - service: valve.close_valve
            target:
              entity_id: valve.morpheus_reich_valve_4
          - service: system_log.write
            data:
              message: >
                Dusche: automatisch geschlossen nach
                {{ states('input_number.dusche_laufzeit') | int }} Min
              level: info
    

    Dashboard-Button (Lovelace YAML)

    square: false
    type: grid
    cards:
      - type: custom:button-card
        entity: valve.morpheus_reich_valve_4
        name: Dusche
        icon: mdi:shower-head
        show_state: true
        size: 22px
        state_display: |
          [[[
            return entity.state === 'open' ? 'Ein' : 'Aus';
          ]]]
        tap_action:
          action: toggle
        hold_action:
          action: more-info
        state:
          - value: open
            styles:
              card:
                - background: "#2e7d32"
                - border: 2px solid "#2e7d32"
              icon:
                - color: "#ffffff"
              name:
                - color: "#ffffff"
              state:
                - color: "#e8f5e9"
          - value: closed
            styles:
              card:
                - background: "#3a3a3a"
                - border: "1px solid #666"
              icon:
                - color: "#9e9e9e"
              name:
                - color: "#cccccc"
              state:
                - color: "#aaaaaa"
        styles:
          card:
            - height: 60px
            - padding: 6px 10px
            - border-radius: 10px
            - background: var(--ha-card-background)
            - border: 1px solid var(--divider-color)
            - cursor: pointer
          grid:
            - grid-template-areas: "\"i n\" \"i s\""
            - grid-template-columns: 32px auto
            - grid-template-rows: auto auto
            - align-items: center
            - justify-items: start
          icon:
            - width: 22px
            - height: 22px
            - color: var(--secondary-text-color)
          name:
            - font-size: 14px
            - font-weight: 600
            - justify-self: start
            - text-align: left
          state:
            - font-size: 12px
            - opacity: 0.8
            - justify-self: start
            - text-align: left
      - type: custom:button-card
        name: Dauer
        icon: mdi:timer
        show_name: false
        entity: input_number.dusche_laufzeit
        tap_action:
          action: call-service
          service: input_number.increment
          target:
            entity_id: input_number.dusche_laufzeit
        hold_action:
          action: call-service
          service: input_number.decrement
          target:
            entity_id: input_number.dusche_laufzeit
        double_tap_action:
          action: more-info
        show_state: true
        state_display: |
          [[[
            const v = Number(entity.state);
            return `${v} min`;
          ]]]
        aspect_ratio: 3/1
        size: 22px
        styles:
          card:
            - height: 60px
            - padding: 6px 10px
            - border-radius: 10px
            - border: 1px solid
            - background: |
                [[[
                  const v = Number(entity.state);
                  if (v === 0) return '#424242';       // aus / deaktiviert
                  if (v <= 90) return '#2e7d32';        // grün (kurz, sicher)
                  if (v <= 150) return '#f9a825';       // gelb (normal)
                  return '#c62828';                    // rot (lang)
                ]]]
          name:
            - font-size: 14px
            - font-weight: 600
            - color: "#cccccc"
          icon:
            - width: 22px
            - height: 22px
            - color: "#bdbdbd"
          state:
            - font-size: 14px
            - font-weight: bold
            - color: "#ffffff"
    columns: 2
    

    ✅ Fazit

    Mit relativ wenig Aufwand ist aus einem gewöhnlichen Garten-Jacuzzi und einer Dusche eine vollständig smarte Wellness-Oase geworden. Der Jacuzzi heizt praktisch kostenlos mit Solarstrom, filtert das Wasser zuverlässig auch an sonnenarmen Tagen – und schaltet sich bei manueller Nutzung einfach gar nicht erst in den Weg. Die Gartendusche vergisst sich nicht mehr von selbst.

    Was mich dabei am meisten überzeugt: Das System ist robust gegenüber manuellen Eingriffen. Wer den Spa von Hand einschaltet, wird nicht von der Automatisierung überrascht. Wer die Dusche früher zudreht als geplant, muss sich nicht um den laufenden Timer sorgen. Die Automatisierung hilft – sie übernimmt nicht die Kontrolle.

    Hast du ähnliche Geräte im Garten? Ich freue mich über Fragen und Feedback in den Kommentaren! 👇