Thought it was about time I shared my project and code to foster ideas and give people another example of what can be done with a handful of parts. Note that my project involves mains voltages so I won’t be showing how to wire it up. But in nutshell, I’m using opto-isolated relay (and this is a must with mains voltages) to switch the Spa pool old controller board’s (which is blown hence this project) safety relay at 12vdc from pin D0.
The rotary encoder allows the set point temperature to be changed on the fly and pressing it turns the pump off by opening the safety relay.
The screen has two pages, cycled every 5 seconds, one showing the thermostat info and the other showing the time and outside temperature from my Cumulus powered weather station by using MQTT
Currently pin D5 is unused but I’ve left it in the code.
Special thanks to youtuber 3ATIVE VFX Studio whos code was the basis for this (but wouldn’t compile for me so I stripped it back a bit) and ssieb, Jos and Jessie over on Discord!
The code below is now out of date and likely won't work anymore due to upgrades in ESPHome.
Please see the code in a later post for up to date and working code.
esphome:
name: spa-thermostat
esp8266:
board: d1_mini
# Enable logging
logger:
level: DEBUG
# Enable Home Assistant API
api:
ota:
password: "1f6ed1154bb09c5397e3979920ee71bc"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Spa-Thermostat Fallback Hotspot"
password: "DzBH9uwq0q10"
captive_portal:
dallas:
- pin: D3
update_interval: 3s
i2c:
time:
- platform: homeassistant
id: esptime
text_sensor:
- platform: homeassistant
name: "Weather_Station_Temp"
id: "Weather_Station_Temp"
entity_id: sensor.weather_station_temp
font:
- file: "_fonts/refsan.ttf"
id: my_font
size: 32
display:
platform: ssd1306_i2c
model: SH1106 128x64
id: my_display
#reset_pin D0
address: 0x3C
rotation: 180°
pages:
- id: page1
lambda: |-
if (id(temperature).has_state()) {
it.printf(0, 0, id(my_font),"c:" "%.1f°", id(temperature).state);
}
it.printf(0, 31, id(my_font),"s:" "%.1f°", id(spa_thermostat).target_temperature_low);
- id: page2
lambda: |-
it.printf(0, 0, id(my_font), "o:%s", id(Weather_Station_Temp).state.c_str());
it.strftime(0, 60, id(my_font), TextAlign::BASELINE_LEFT, "%H:%M", id(esptime).now());
interval:
- interval: 5s
then:
- display.page.show_next: my_display
- component.update: my_display
switch:
- platform: gpio
pin: D0
id: heater
inverted: false
- platform: gpio
pin: D5
id: pump
inverted: false
binary_sensor:
- platform: gpio
id: button
pin:
number: D4
inverted: True
mode: INPUT_PULLUP
on_press:
then:
- if:
condition:
lambda: 'return id(spa_thermostat).mode == CLIMATE_MODE_HEAT;'
then:
- logger.log: "Current state HA-switch is HEATING/IDLE -> switch to OFF"
- climate.control:
id: spa_thermostat
mode: "OFF"
else:
- logger.log: "Current state HA-switch is OFF -> switch to HEATING/IDLE"
- climate.control:
id: spa_thermostat
mode: "HEAT"
web_server:
port: 80
climate:
- platform: thermostat
name: "Spa Temperature Controller"
id: spa_thermostat
visual:
min_temperature: 30 °C
max_temperature: 45°C
temperature_step: 0.1 °C
sensor: temperature
default_target_temperature_low: 38.2 °C
min_heating_off_time: 300s
min_heating_run_time: 120s
min_idle_time: 300s
heat_overrun: 0.1 °C
heat_action:
- switch.turn_on: heater
idle_action:
- switch.turn_off: heater
sensor:
- platform: dallas
address: 0xb4000008f9101d28
name: "temperature"
id: temperature
- platform: rotary_encoder
id: encoder
pin_a:
number: D6
mode: INPUT_PULLUP
pin_b:
number: D7
mode: INPUT_PULLUP
on_clockwise:
- climate.control:
id: spa_thermostat
target_temperature: !lambda "return id(spa_thermostat).target_temperature_low + 0.1;"
on_anticlockwise:
- climate.control:
id: spa_thermostat
target_temperature: !lambda "return id(spa_thermostat).target_temperature_low - 0.1;"
Happy soaking!