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

🛁 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.

⚠️ 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
🔌 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_reachedist 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.

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_toggleauf, das Heizung und Filter gemeinsam schaltet. - Filterpumpe – blau wenn aktiv, dunkel wenn aus. Arbeitet über den Template-Switch
switch.v8_jacuzzi_spa_filter_uials 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.

📌 Ü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.

⚡ 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.

📋 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_stateUnter: „/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_stateVollstä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.

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.

📋 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! 👇

















