Hello,
I want to create this in ESPHome and am mentally at the beginning and end of it at the same time.
I would like to have a thermostat that switches a switch template.
The template should compare the current and target temperatures and switch various output relays based on the difference.
Target temperature 43°C = thermostat value
Current temperature 20°C = temperature sensor (1-Wire)
Difference greater than 10°C
switch output relays 1, 2, and 3
Difference less than 5°C
switch output relays 1, 2, and 3
Difference less than 3°C
switch output relay 1
Difference 0°C
switch all relays off
I know it’s possible with Lambda calls, but my horizons are unfortunately far too narrow.
If anyone has something like this, I would be very happy to get it
or if someone can help me.
Thank you, best regards,
Gerald
Edwin_D
(Edwin D.)
October 5, 2025, 5:05pm
2
Seems like this would help as a starting point, but you’d have to add some extra logic for the stages:
This is exactly the logic at stake.
Karosm
(Karosm)
October 5, 2025, 6:44pm
4
Afaik Esphome doesn’t have multi-point thermostat.
You can obviously do it by yourself, but without using labdas it’s not so straightforward.
You could do it with:
-switches
-one wire temp sensor
-template number for setpoint
-template sensor to for differential value and all the automations
gerald_FS
(Gerald)
October 6, 2025, 10:48pm
5
Thanks to AI (ChatGPT) I got my controls, they work and I’m happy!
substitutions:
device_name: nand11-sauna32
friendly_name: Sauna32
device_description: Saunasteuerung II
esphome:
name: ${device_name}
comment: ${device_description}
friendly_name: ${friendly_name}
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: !secret api_password
# Enable over-the-air updates
ota:
platform: esphome
password: !secret ota_password
# Web server
web_server:
port: 80
auth:
username: !secret webuser
password: !secret webpasswort
# Sync time with Home Assistant
time:
- platform: homeassistant
id: homeassistant_time
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
domain: !secret domain
ap:
ssid: ${device_name}_fallback
password: !secret ap_password
ap_timeout: 1min
captive_portal:
# Text sensors with general information
text_sensor:
- platform: version
name: "T ${friendly_name} Version"
- platform: wifi_info
ip_address:
name: T ${friendly_name} IP Addresse
- platform: template
name: "T Sauna Restzeit"
id: restzeit_text
icon: "mdi:timer-sync-outline"
- platform: template
name: "T Sauna Heizstufe"
id: heizstufe_text
icon: "mdi:information-outline"
###########
# BME280
# 1-Wire DS1820
# HTU21D = Saunatemperatur (hinten oben)
#
#GPIO32 Relay #1 Interior
#GPIO33 Relay #2 Heizstab 1
#GPIO25 Relay #3 Heizstab 2
#GPIO26 Relay #4 Heizstab 3
#
#
#GPIO32 Relay #1 Interior
#GPIO33 Relay #2 Heizstab 1
#GPIO25 Relay #3 Heizstab 2
#GPIO26 Relay #4 Heizstab 3
#
# 1Wire GPIO13
# I2C
# SDA GPIO14
# SDL GPIO12
#
# Eingänge
# Taster 1 GPIO16 interner PullUp Saunaofen
# Taster 2 GPIO17 interner PullUp Interior
# Statusgeber GPIO18
########################
##i2C - Bus
i2c:
sda: GPIO14
scl: GPIO12
scan: true
# --- 1-Wire Temperatursensor ---
one_wire:
- platform: gpio
pin: GPIO13
## I2C Sensor - BME280 (Raumtemperatur / Feuchte)
sensor:
- platform: bme280_i2c
temperature:
name: "S Temperatur (Keller)"
id: bme280_t
pressure:
name: "S Luftdruck (Keller)"
id: bme280_pressure
humidity:
name: "S Luftfeuchtigkeit (Keller)"
id: bme280_luftfeuchte
address: 0x76
update_interval: 15s
## I2C Sensor - HTU21d (Temperatur / Feuchte - Sauna hinten oben)
- platform: htu21d
temperature:
name: "S Saunatemperatur"
id: htu
## Abschaltung wenn 105°C überschritten ist
on_value_range:
- above: 105.0
then:
- switch.turn_off: relais_stufe1
- switch.turn_off: relais_stufe2
- switch.turn_off: relais_stufe3
- switch.turn_off: sauna_switch
- binary_sensor.template.publish:
id: sauna_overtemp
state: ON
humidity:
name: "S Feuchtigkeit"
update_interval: 15s
- platform: sht3xd
temperature:
name: "S Sauna SHT"
id: sauna_sht
humidity:
name: "S Sauna SHT F"
address: 0x44
update_interval: 30s
- platform: dallas_temp
# address: 0xXXXXXXXXXXXXXXX # hier deine Sensor-Adresse eintragen
name: "S Sauna Temperatur"
id: ist_temp
update_interval: 1s
## Sichereitsabschaltung wenn 120°C vorne überschritten ist
on_value_range:
- above: 120.0
then:
- switch.turn_off: relais_stufe1
- switch.turn_off: relais_stufe2
- switch.turn_off: relais_stufe3
- switch.turn_off: sauna_switch
- binary_sensor.template.publish:
id: sauna_overtemp
state: ON
- platform: wifi_signal
name: "T WiFi Signal Sensor"
update_interval: 30s
# --- Eingabefeld in Home Assistant (Solltemp & Timerzeit) ---
number:
- platform: template
name: "E Saunatemperatur"
id: soll_temp
icon: "mdi:temperature-celsius"
min_value: 25
max_value: 105
step: 1
unit_of_measurement: "°C"
optimistic: true
initial_value: 60
- platform: template
name: "E Sauna Laufzeit (Minuten)"
id: sauna_timer_minutes
icon: "mdi:timer-play-outline"
min_value: 1
max_value: 240
step: 1
unit_of_measurement: "min"
optimistic: true
initial_value: 150 # ⏱️ Startwert = 150 Minuten
on_value:
then:
- lambda: |-
if (id(thermostat_aktiv)) {
int neue_laufzeit = id(sauna_timer_minutes).state * 60;
int differenz = neue_laufzeit - id(timer_restzeit);
id(timer_restzeit) += differenz;
ESP_LOGI("main", "Laufzeit angepasst – Restzeit jetzt: %d Sekunden", id(timer_restzeit));
}
button:
- platform: restart
name: "T Sauna Reboot"
id: sauna_reboot
icon: "mdi:power-cycle"
- platform: template
name: "T Sauna Start/Stopp"
id: sauna_toggle
icon: "mdi:fire"
on_press:
- lambda: |-
id(thermostat_aktiv) = !id(thermostat_aktiv);
if (id(thermostat_aktiv)) {
id(timer_restzeit) = id(sauna_timer_minutes).state * 60;
ESP_LOGI("main", "Thermostat über Taster/Software gestartet");
} else {
id(heizung_aktiv) = false;
id(relais_stufe1).turn_off();
id(relais_stufe2).turn_off();
id(relais_stufe3).turn_off();
ESP_LOGI("main", "Thermostat über Taster/Software gestoppt");
}
- platform: template
name: "T Interior"
id: interior_toggle
icon: "mdi:lightbulb-on-outline"
on_press:
- switch.toggle: interior
# --- Relais-Ausgänge ---
switch:
- platform: gpio
name: "R1 - Interior"
id: interior
icon: "mdi:music-note-bluetooth"
inverted: false
restore_mode: always_off
pin: GPIO32
#### Heizstäbe einzeln ####
- platform: gpio
id: relais_stufe1
name: "Heizstab 1"
pin: GPIO33
icon: "mdi:radiator"
inverted: false
restore_mode: always_off
- platform: gpio
id: relais_stufe2
name: "Heizstab 2"
pin: GPIO25
icon: "mdi:radiator"
inverted: false
restore_mode: always_off
- platform: gpio
id: relais_stufe3
name: "Heizstab 3"
pin: GPIO26
icon: "mdi:radiator"
inverted: false
restore_mode: always_off
# --- Software-Taster (Home Assistant) ---
- platform: template
name: "Sauna Thermostat"
id: sauna_switch
icon: "mdi:fire"
optimistic: true
turn_on_action:
- lambda: |-
id(thermostat_aktiv) = true;
id(timer_restzeit) = id(sauna_timer_minutes).state * 60;
ESP_LOGI("main", "Thermostat per Software eingeschaltet");
turn_off_action:
- lambda: |-
id(thermostat_aktiv) = false;
id(relais_stufe1).turn_off();
id(relais_stufe2).turn_off();
id(relais_stufe3).turn_off();
ESP_LOGI("main", "Thermostat per Software ausgeschaltet");
# --- Taster zum Ein/Aus-Schalten ---
binary_sensor:
- platform: gpio
pin:
number: GPIO16
mode: INPUT_PULLUP
inverted: true
name: "Taster Sauna"
id: sauna_taster
on_press:
then:
- lambda: |-
id(thermostat_aktiv) = !id(thermostat_aktiv);
if (id(thermostat_aktiv)) {
ESP_LOGI("main", "Thermostat eingeschaltet");
id(timer_restzeit) = id(sauna_timer_minutes).state * 60;
} else {
ESP_LOGI("main", "Thermostat ausgeschaltet");
id(relais_stufe1).turn_off();
id(relais_stufe2).turn_off();
id(relais_stufe3).turn_off();
}
- platform: gpio
name: "Taster - Interior"
pin:
number: GPIO17
inverted: true
mode:
input: true
pullup: true
on_press:
then:
- switch.toggle: interior
- platform: template
name: "T Sauna Übertemperatur"
id: sauna_overtemp
device_class: safety
# --- Hilfsvariablen ---
globals:
- id: thermostat_aktiv
type: bool
restore_value: no
initial_value: 'false'
- id: timer_restzeit
type: int
restore_value: no
initial_value: '0'
- id: heizung_aktiv
type: bool
restore_value: no
initial_value: 'false'
# --- Regel-Logik ---
interval:
- interval: 1s
then:
- if:
condition:
lambda: 'return id(thermostat_aktiv);'
then:
- lambda: |-
float ist = id(ist_temp).state;
float soll = id(soll_temp).state;
float diff = soll - ist;
// --- Hysterese ---
if (id(heizung_aktiv)) {
if (ist >= soll + 0.1) {
id(heizung_aktiv) = false;
id(relais_stufe1).turn_off();
id(relais_stufe2).turn_off();
id(relais_stufe3).turn_off();
id(heizstufe_text).publish_state("Zieltemperatur");
ESP_LOGI("main", "Zieltemperatur");
}
} else {
if (ist <= soll - 1.0) {
id(heizung_aktiv) = true;
ESP_LOGI("main", "Heizung wieder aktiviert (unter Soll -1°C)");
}
}
// --- Stufenlogik nur wenn Heizung aktiv ---
if (id(heizung_aktiv)) {
if (diff > 20) {
id(relais_stufe1).turn_on();
id(relais_stufe2).turn_on();
id(relais_stufe3).turn_on();
id(heizstufe_text).publish_state("Stufe 3");
} else if (diff > 10) {
id(relais_stufe1).turn_on();
id(relais_stufe2).turn_on();
id(relais_stufe3).turn_off();
id(heizstufe_text).publish_state("Stufe 2");
} else if (diff > 5) {
id(relais_stufe1).turn_on();
id(relais_stufe2).turn_off();
id(relais_stufe3).turn_off();
id(heizstufe_text).publish_state("Stufe 1");
} else {
// nahe Solltemperatur -> nur Stufe 1 bleibt an
id(relais_stufe1).turn_on();
id(relais_stufe2).turn_off();
id(relais_stufe3).turn_off();
id(heizstufe_text).publish_state("Stufe 1 (Feinregelung)");
}
}
// --- Timer ---
if (id(timer_restzeit) > 0) {
id(timer_restzeit) -= 1;
}
// --- Wenn Zeit abgelaufen ist ---
if (id(timer_restzeit) <= 0 && id(thermostat_aktiv)) {
id(timer_restzeit) = 0;
id(thermostat_aktiv) = false;
id(heizung_aktiv) = false;
id(relais_stufe1).turn_off();
id(relais_stufe2).turn_off();
id(relais_stufe3).turn_off();
id(sauna_switch).turn_off();
id(heizstufe_text).publish_state("Aus (Timer abgelaufen)");
id(restzeit_text).publish_state("0 sec");
ESP_LOGI("main", "Sauna-Laufzeit abgelaufen – alles aus");
}
// --- Restzeit anzeigen ---
char buffer[32];
if (id(timer_restzeit) > 59) {
sprintf(buffer, "%d min", id(timer_restzeit) / 60);
} else {
sprintf(buffer, "%d sec", id(timer_restzeit));
}
id(restzeit_text).publish_state(buffer);
else:
- lambda: |-
id(relais_stufe1).turn_off();
id(relais_stufe2).turn_off();
id(relais_stufe3).turn_off();
id(heizstufe_text).publish_state("Aus");