The “Safe-Fire” ESPHome Configuration
Features: 45m Auto-Off, 9m/15m Cool-down Lockouts, 90m Emergency Master Trip-Wire, and Status Reporting.
This configuration provides a fail-safe control system for gas fireplaces using ESPHome. It is designed to work with the Home Assistant Generic Thermostat while adding hardware-level safety guards that work even if Home Assistant or Wi-Fi is offline.
Key Safety Features
- 45-Minute Max Run (Hard Stop): Automatically shuts off the fireplace after 45 minutes of continuous use to prevent overheating or accidentally leaving it on.
- Enforced Cool-Down (15 min): After a 45-minute run, the system enters a mandatory 15-minute “lockout” where ignition is blocked, allowing components to cool.
- Short-Cycle Protection (9 min): Prevents the gas valve from rapidly clicking on/off if the thermostat fluctuates near the target temperature.
- Emergency Trip-Wire (90 min): If the fireplace remains on for 90 minutes for any reason, the system triggers a permanent “Emergency Shutdown.” This kills the relay and requires a human to manually flip a “Master Reset” switch in the UI before it can be used again.
- Power-Loss Safety: Defaults to
ALWAYS_OFFon reboot so the fireplace never ignites unexpectedly after a power flicker.
Setup Requirements
- Hardware: ESP32 or ESP8266 and a Relay module.
- ESPHome Components:
template_switch,script(inmode: single), andglobals. - Home Assistant: Recommended
min_cycle_durationof 10 minutes andkeep_aliveof 17 minutes to sync with hardware timers.
# Safety Fireplace Controller for ESPHome
# Logic:
# 1. 45-minute max run time with 15-minute cool-down.
# 2. 9-minute short-cycle protection (synced with HA 10-minute cycle).
# 3. 90-minute Emergency Master Shutdown (Requires manual reset).
globals:
- id: lockout_active
type: bool
initial_value: 'false'
- id: master_disabled
type: bool
restore_value: yes # Remembers trip state after power loss
initial_value: 'false'
switch:
- platform: gpio
pin: GPIO12 # Change to your relay pin
id: relay_output
internal: true
restore_mode: ALWAYS_OFF
- platform: template
name: "Fireplace Master Enable"
id: master_enable_switch
optimistic: true
icon: "mdi:shield-check"
on_turn_on:
- globals.set: {id: master_disabled, value: 'false'}
on_turn_off:
- globals.set: {id: master_disabled, value: 'true'}
- switch.turn_off: relay_output
- platform: template
name: "Fireplace Control"
id: fireplace_switch
icon: "mdi:fireplace"
lambda: "return id(relay_output).state;"
turn_on_action:
- if:
condition:
and:
- lambda: 'return !id(master_disabled);'
- lambda: 'return !id(lockout_active);'
then:
- switch.turn_on: relay_output
- script.execute: safety_timer
- script.execute: emergency_watchdog
else:
- logger.log: "Ignition BLOCKED: Safety check failed."
turn_off_action:
- switch.turn_off: relay_output
- script.stop: safety_timer
- script.stop: emergency_watchdog
- if: # Only trigger short-cycle lockout if we didn't hit the 45m hard-stop
condition:
lambda: 'return !id(lockout_active);'
then:
- script.execute: force_off_timer
text_sensor:
- platform: template
name: "Fireplace Status"
update_interval: 5s
lambda: |-
if (id(master_disabled)) return {"EMERGENCY TRIP - RESET REQ"};
if (id(relay_output).state) return {"Heating"};
if (id(lockout_active)) return {"Safety Lockout (Cooling)"};
return {"Ready"};
script:
- id: safety_timer
mode: single # Prevents HA keep-alive from resetting the 45m clock
then:
- delay: 45min
- switch.turn_off: relay_output
- globals.set: {id: lockout_active, value: 'true'}
- delay: 15min
- globals.set: {id: lockout_active, value: 'false'}
- id: force_off_timer
mode: restart
then:
- globals.set: {id: lockout_active, value: 'true'}
- delay: 9min
- globals.set: {id: lockout_active, value: 'false'}
- id: emergency_watchdog
mode: single
then:
- delay: 90min
- switch.turn_off: relay_output
- globals.set: {id: master_disabled, value: 'true'}
- switch.turn_off: master_enable_switch