|
#substitutions is a list of variables basically. here you can easily change the high/low limits and setpoints |
|
substitutions: |
|
temperature_setpoint: '25.5' |
|
temperature_alarm_low: '23.3' |
|
temperature_alarm_high: '27.5' |
|
temperature_hysteresis: '0.3' |
|
|
|
esphome: |
|
name: reefbrain |
|
platform: ESP32 |
|
board: nodemcu-32s |
|
on_boot: |
|
priority: -10 |
|
# … |
|
then: |
|
#on boot we turn the thermostat on ("HEAT" mode in my case) and set the desired setpoint |
|
– climate.control: |
|
id: reef_temp_control |
|
mode: HEAT |
|
target_temperature: ${temperature_setpoint} |
|
#This script begins the monitor that makes sure we receive a new temperature measurement at least every 2 minutes |
|
#If this script isn't called every 2 minutes it will start the high temperature alarm |
|
#Everytime we call the script the timer resets. I call this the temperature "heartbeat" |
|
– script.execute: temperature_heartbeat |
|
|
|
wifi: |
|
ssid: "Wifi" |
|
password: "Wifi_Password" |
|
# Enable fallback hotspot (captive portal) in case wifi connection fails |
|
ap: |
|
ssid: "Reefbrain Fallback Hotspot" |
|
password: "MRF8tFN7ooul" |
|
#use_address: 192.168.1.20 |
|
#You can delete the manual IP config if you want IP address to be assigned automatically |
|
manual_ip: |
|
static_ip: 192.168.1.41 |
|
gateway: 192.168.1.1 |
|
subnet: 255.255.255.0 |
|
|
|
captive_portal: |
|
|
|
# Enable logging |
|
logger: |
|
|
|
# Enable Home Assistant API |
|
api: |
|
|
|
#I highly recommend passwords! It will prevent you from sending the wrong firmware to the wrong device. |
|
ota: |
|
password: reefbrain |
|
|
|
dallas: |
|
#Temperature sensors |
|
– pin: GPIO32 |
|
update_interval: 15s |
|
|
|
sensor: |
|
|
|
#Temperature Sensor 1 |
|
– platform: dallas |
|
address: 0xB1031297794BF628 |
|
id: reef_temp_1 |
|
name: "Reef Temperature 1" |
|
filters: |
|
#I manually calibrated my temperature sensors and figured out this offset |
|
– offset: 1.14 |
|
– filter_out: nan |
|
– sliding_window_moving_average: |
|
window_size: 4 |
|
send_every: 4 |
|
#the next section triggers alarms and the temperature heartbeat |
|
#every time a valid value is received (anything besides NaN) this section is called |
|
on_value: |
|
then: |
|
– if: #if temperature is in range, go ahead and reset the temperature heartbeat timer |
|
condition: |
|
sensor.in_range: |
|
id: reef_temp_1 |
|
above: ${temperature_alarm_low} |
|
below: ${temperature_alarm_high} |
|
then: |
|
– script.execute: temperature_heartbeat |
|
– if: #if temperature is below the low limit then trigger the low alarm |
|
condition: |
|
sensor.in_range: |
|
id: reef_temp_1 |
|
below: ${temperature_alarm_low} |
|
then: |
|
– switch.turn_on: alarm_temperature_low |
|
– if: #if temperature is above the high limit then trigger the high alarm |
|
condition: |
|
sensor.in_range: |
|
id: reef_temp_1 |
|
above: ${temperature_alarm_high} |
|
then: |
|
– switch.turn_on: alarm_temperature_high |
|
|
|
#Temperature Sensor 2 |
|
– platform: dallas |
|
address: 0x86031497798E6228 |
|
id: reef_temp_2 |
|
name: "Reef Temperature 2" |
|
filters: |
|
#I manually calibrated my temperature sensors and figured out this offset |
|
– offset: 2.04 |
|
– filter_out: nan |
|
– sliding_window_moving_average: |
|
window_size: 4 |
|
send_every: 4 |
|
#for the second (redundant) temperature sensor I only monitor for the high temperature alarm |
|
on_value: |
|
then: |
|
– if: |
|
condition: |
|
sensor.in_range: |
|
id: reef_temp_2 |
|
above: ${temperature_alarm_high} |
|
then: |
|
– switch.turn_on: alarm_temperature_high |
|
|
|
|
|
switch: |
|
|
|
#Heater SSR |
|
– platform: gpio |
|
id: heater_ssr |
|
pin: GPIO0 |
|
#I made this internal because the user shouldn't manually manipulate the heater – just the thermostat controller and alarms |
|
internal: true |
|
name: "Heater SSR" |
|
|
|
#Making the alarms switches means the microcontroller can trigger the switch on and do the appropriate behavior while |
|
#the user can easily turn it off via Home Assistant |
|
|
|
#High Temperature Alarm |
|
– platform: template |
|
name: "High Temperature Alarm" |
|
id: alarm_temperature_high |
|
#if you don't set optimistic to true then the switch automatically turns itself off |
|
optimistic: true |
|
on_turn_on: |
|
– logger.log: "High Temperature Alarm Turned On!" |
|
#change climate control mode to "OFF" |
|
– climate.control: |
|
id: reef_temp_control |
|
mode: "OFF" |
|
#not sure if this is 100% necessary but just to be safe turn off the SSR |
|
– switch.turn_off: heater_ssr |
|
#this while loop runs for as long as the alarm is left on. |
|
#once the user turns the alarm switch off it will exit the while loop |
|
– while: |
|
condition: |
|
switch.is_on: alarm_temperature_high |
|
then: |
|
– logger.log: "High Temperature Alarm Still On!" |
|
– script.execute: triple_siren_short |
|
– delay: 90s |
|
#once the user turns the alarm off we turn the thermostat controller back on i.e. "HEAT" |
|
#the siren script just gives a nice audible feedback that the alarm has been acknowledged/shut off |
|
on_turn_off: |
|
– logger.log: "High Temperature Alarm Turned Off!" |
|
– script.execute: single_siren_short |
|
– climate.control: |
|
id: reef_temp_control |
|
mode: "HEAT" |
|
|
|
#Low Temperature Alarm |
|
#This alarm doesn't stop the thermostat, it just sets off a siren. |
|
#One state you might run into is when the system is starting up you may be significantly below the low alarm |
|
#In that case you still want the thermostat to control, so we don't turn off the thermostat here. |
|
#For now this has to be acknowledged before turning off (rather than automatically turning off when in range) |
|
– platform: template |
|
name: "Low Temperature Alarm" |
|
id: alarm_temperature_low |
|
optimistic: true |
|
on_turn_on: |
|
– logger.log: "Low Temperature Alarm Turned On!" |
|
– while: |
|
condition: |
|
switch.is_on: alarm_temperature_low |
|
then: |
|
– logger.log: "Low Temperature Alarm Still On!" |
|
– script.execute: triple_siren_short |
|
– delay: 90s |
|
on_turn_off: |
|
– logger.log: "Low Temperature Alarm Turned Off!" |
|
– script.execute: single_siren_short |
|
|
|
#Siren Output |
|
– platform: gpio |
|
pin: GPIO15 |
|
id: siren |
|
name: "Siren" |
|
#I made this internal because there's no real usefulness in the user turning it on/off |
|
#The alarms and scripts manage turning it on/off. |
|
internal: true |
|
|
|
climate: |
|
– platform: thermostat |
|
id: reef_temp_control |
|
#Note that here the same temperature sensor that has all of the alarms configured is used to control the thermostat |
|
sensor: reef_temp_1 |
|
default_target_temperature_low: ${temperature_setpoint} |
|
hysteresis: ${temperature_hysteresis} |
|
heat_action: |
|
– switch.turn_on: heater_ssr |
|
idle_action: |
|
– switch.turn_off: heater_ssr |
|
|
|
script: |
|
#the single siren is useful as an acknowledge noise. just one simple chirp |
|
#the scirpt mode has to be queued or else you can't use it properly in the next script |
|
– id: single_siren_short |
|
mode: queued |
|
then: |
|
– switch.turn_on: siren |
|
– delay: 50ms |
|
– switch.turn_off: siren |
|
– delay: 50ms |
|
|
|
#the triple chirp is used as the alarm and always implies something is wrong |
|
– id: triple_siren_short |
|
then: |
|
– script.execute: single_siren_short |
|
– script.execute: single_siren_short |
|
– script.execute: single_siren_short |
|
|
|
#this script monitors the frequency that we receive new temperature measurements |
|
#if a temperature sensor fails we don't want the thermostat to be stuck high and cook the aquarium |
|
#so the "heartbeat" idea is is that we want a consistent flow of incoming temperature measurements |
|
#if not we should safely shut down and turn on an alarm. |
|
#this script is basically always running. everytime we execute the script is restarts. |
|
#but if we go 2 minutes without restarting it then it will successfully complete and turn on the high alarm |
|
#so everytime the sensor reads a value it will restart this timer. |
|
– id: temperature_heartbeat |
|
mode: restart |
|
then: |
|
– delay: 2 min |
|
– switch.turn_on: alarm_temperature_high |
|
|