Same here , don’t want it flash the firmware , perhaps an esp32 that can capture data and relay over WiFi or a raspberry pi or something . Would be nice to have data trend
Has anyone with a newer board revision tried flashing this with ESPHome? If not, looks like I may be the first… hoping it works.
Anything I should be on the look out for or things I can check to confirm any changes to the board haven’t effected our use here?
The unit I just received came with board version REV:A 2022.05.28 and looks mostly the same from what I saw at first glance. Hoping maybe it was just a very minor change that will have no effect. Wish me luck I guess.
Building upon the earlier solution @TCecil the following configuration supports both a 2 and 3 speed fan configuration. Earlier solutions were for just 3 speed fans but I have a 2 speed (no Medium speed).
My solution also adds a switch
that support automatic control of the fan state/speed based on the current temperature and a set of number
components.
This allows you to set a High, Medium, Low, Off temperatures and enable “Auto Speed Management”. You can even bind them in Home Assistant to UI elements to make a nice management console.
The benefit of this solution over using Home Assistant automations is that the fan will continue to automatically control its state based on the current temperature even if it loses wifi connectivity completely.
esphome:
name: attic-fan
friendly_name: Attic # just name Attic and not Attic Fan so outputs like Temperature become "Attic Temperature" and not "Attic Fan Temperature"
on_boot: #Display cascading LED's to show full boot
priority: 800 #run after full boot
then:
- delay: 1s
- light.turn_on: temp_hum_led_light
- light.turn_on: speed_led_light
- light.turn_on: bluetooth_led_light
- delay: 1s
- light.turn_off: bluetooth_led_light
- delay: 0.5s
- light.turn_off: speed_led_light
- delay: 0.5s
- light.turn_off: temp_hum_led_light
on_loop:
then:
- lambda: |-
//force fan component to sync with speed select component
if (id(on_off_switch).state && id(attic_fan).speed != id(speed_mode) && id(speed_mode) > 0){
auto call = id(attic_fan).turn_on();
call.set_speed(id(speed_mode));
call.perform();
}
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API
api:
ota:
- platform: esphome
password: "1234"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Attic-Fan"
password: "1234"
captive_portal:
globals:
- id: speed_mode
type: int
initial_value: '1'
- id: timer_offset
type: long
initial_value: '0'
- id: timer_stop
type: long
initial_value: '0'
- id: timer_en
type: bool
initial_value: 'false'
- id: select_lock
type: bool
initial_value: 'false'
- id: failsafe_temp
type: float
initial_value: '182'
# set to 'true' if your Fan supports 3 speeds (also update lines below with comments as well)
- id: three_speed_fan
type: bool
initial_value: 'false'
time:
- platform: homeassistant
id: homeassistant_time
number:
- platform: template
id: high_speed_above_temp
device_class: temperature
entity_category: config
unit_of_measurement: "°F"
name: "Fan High Speed On Temp"
restore_value: True
min_value: 77
max_value: 125
initial_value: 100
optimistic: True
step: 1
set_action:
- if:
condition:
lambda: 'return id(auto_temp_control).state && id(attic_temperature).state >= x && id(attic_temperature).state < id(failsafe_temp);'
then:
- fan.turn_on:
id: attic_fan
speed: !lambda "return id(three_speed_fan) ? 3 : 2;"
- if:
condition:
lambda: 'return id(auto_temp_control).state && id(attic_temperature).state < x && id(attic_temperature).state >= (id(three_speed_fan) ? id(medium_speed_above_temp).state : id(low_speed_above_temp).state) && id(attic_temperature).state < id(failsafe_temp);'
then:
- fan.turn_on:
id: attic_fan
speed: !lambda "return id(three_speed_fan) ? 2 : 1;"
# Only supported on Fans that have 3 speeds (see the global 'three_speed_fan' for info)
- platform: template
id: medium_speed_above_temp
device_class: temperature
entity_category: config
unit_of_measurement: "°F"
name: "Fan Medium Speed On Temp"
restore_value: True
min_value: 76
max_value: 115
initial_value: 95
optimistic: True
step: 1
set_action:
- if:
condition:
lambda: 'return id(three_speed_fan) && id(auto_temp_control).state && id(attic_temperature).state < id(high_speed_above_temp).state && id(attic_temperature).state >= x && id(attic_temperature).state < id(failsafe_temp);'
then:
- fan.turn_on:
id: attic_fan
speed: 2
- if:
condition:
lambda: 'return id(three_speed_fan) && id(auto_temp_control).state && id(attic_temperature).state < x && id(attic_temperature).state >= id(low_speed_above_temp).state && id(attic_temperature).state < id(failsafe_temp);'
then:
- fan.turn_on:
id: attic_fan
speed: 1
- platform: template
id: low_speed_above_temp
entity_category: config
device_class: temperature
unit_of_measurement: "°F"
name: "Fan Low Speed On Temp"
restore_value: True
min_value: 75
max_value: 110
initial_value: 90
optimistic: True
step: 1
set_action:
- if:
# Turn the Fan to Low Speed if the temp is above the low speed temp and below the high (or medium for 3 speed fans) speed temp (and auto controls are enabled)
condition:
lambda: 'return id(auto_temp_control).state && id(attic_temperature).state < (id(three_speed_fan) ? id(medium_speed_above_temp).state : id(high_speed_above_temp).state) && id(attic_temperature).state >= x && id(attic_temperature).state < id(failsafe_temp);'
then:
- fan.turn_on:
id: attic_fan
speed: 1
- if:
# Turn off the Fan if it was previously on because its temp was above the previous low_speed_above_temp but is now below it
condition:
lambda: 'return id(auto_temp_control).state && id(attic_temperature).state < x && id(attic_temperature).state >= id(low_speed_above_temp).state;'
then:
- fan.turn_off: attic_fan
- platform: template
id: off_below_temp
device_class: temperature
unit_of_measurement: "°F"
name: "Fan Off Temp"
restore_value: True
entity_category: config
min_value: 65
max_value: 100
initial_value: 85
optimistic: True
step: 1
set_action:
- if:
# Turns off the fan if the current temp is below the Fan Off Temp
condition:
lambda: 'return id(auto_temp_control).state && id(attic_temperature).state < x;'
then:
- fan.turn_off: attic_fan
select:
- platform: template
name: "Fan Speed Select"
id: speed_select
update_interval: 6s
options:
- "Low"
# Uncomment for 3 speed fans
# - "Medium"
- "High"
set_action:
- lambda: |-
int prev_mode = id(speed_mode);
if (x == "Low") {
id(speed_mode) = 1;
} else if (x == "High" && id(three_speed_fan)) {
id(speed_mode) = 3;
} else {
id(speed_mode) = 2;
}
if (id(speed_mode) != prev_mode && id(on_off_switch).state){
if (id(speed_mode) == 1){
id(Speed_Low).turn_on();
} else if (id(speed_mode) == 2) {
if (id(three_speed_fan))
id(Speed_Medium).turn_on();
else
id(Speed_High).turn_on();
} else if (id(speed_mode) == 3) {
id(Speed_High).turn_on();
}
}
//if speed is changed in the middle of operation change fan component output to match
//select_lock will prevent excessive commands sent to fan compnent in the turn on process
if (id(on_off_switch).state && (id(attic_fan).speed != id(speed_mode) || id(speed_mode) != prev_mode) && !id(select_lock)){
auto call = id(attic_fan).turn_on();
call.set_speed(id(speed_mode));
call.perform();
} else if (id(select_lock)){
//clear select_lock if it sets to true
id(select_lock) = false;
}
lambda: |-
if (id(Speed_Low).state && id(speed_select).state != "Low") {
return {"Low"};
} else if (id(Speed_Medium).state && id(speed_select).state != "Medium") {
return {"Medium"};
} else if (id(Speed_High).state && id(speed_select).state != "High") {
return {"High"};
}
if(id(speed_select).state == "") {
return {"Low"};
}
return{};
- platform: template
name: "Fan Timer"
id: timer
update_interval: 20s
options:
- "off"
- "1 hour"
- "2 hours"
- "4 hours"
- "8 hours"
- "12 hours"
set_action:
- lambda: |-
if (x == "1 hour"){
id(timer_offset) = 3600;
id(timer_en) = true;
} else if (x == "2 hours"){
id(timer_offset) = 7200;
id(timer_en) = true;
} else if (x == "4 hours"){
id(timer_offset) = 14400;
id(timer_en) = true;
} else if (x == "8 hours"){
id(timer_offset) = 28800;
id(timer_en) = true;
} else if (x == "12 hours"){
id(timer_offset) = 43200;
id(timer_en) = true;
} else if (x == "off") {
id(timer_offset) = 0;
id(timer_en) = false;
}
if (id(timer_en) && id(on_off_switch).state){
id(timer_stop) = id(homeassistant_time).now().timestamp + id(timer_offset); // when the fan should turn off
}
lambda: |-
if (id(timer_en) && id(on_off_switch).state && id(timer_stop) != 0) {
auto time_remaining = id(timer_stop) - id(homeassistant_time).now().timestamp;
if (time_remaining <= 0) {
id(on_off_switch).turn_off();
id(timer_en) = false;
id(time_left).publish_state(0);
return {"off"};
} else if (time_remaining <= 3600 && id(timer).state != "1 hour"){
return {"1 hour"};
} else if (time_remaining > 3600 && time_remaining <= 7200 && id(timer).state != "2 hours"){
return {"2 hours"};
} else if (time_remaining > 7200 && time_remaining <= 14400 && id(timer).state != "4 hours"){
return {"4 hours"};
} else if (time_remaining > 14400 && time_remaining <= 28800 && id(timer).state != "8 hours"){
return {"8 hours"};
} else if (time_remaining > 28800 && time_remaining <= 43200 && id(timer).state != "12 hours"){
return {"12 hours"};
}
}
if (id(timer).state == ""){
return {"off"};
}
return {};
#Front Buttons
binary_sensor:
- platform: gpio
name: "Fan Button 2 - Pair"
pin:
number: 26
inverted: true
mode:
input: true
pullup: true
internal: true
filters:
- delayed_on_off: 100ms
- platform: gpio
name: "Fan Button 1 - Test"
pin:
number: 27
inverted: true
mode:
input: true
pullup: true
internal: true
filters:
- delayed_on_off: 100ms
on_press:
then:
- fan.cycle_speed: attic_fan
light:
#Note: There are 4 LEDs the far left Red LED is tied directly to power and not controllable.
- platform: binary
name: "Fan Right LED - Bluetooth"
output: bluetooth_led
id: bluetooth_led_light
restore_mode: ALWAYS_OFF
internal: true
- platform: binary
name: "Fan Middle LED - Speed"
output: speed_led
id: speed_led_light
restore_mode: ALWAYS_OFF
internal: true
effects: #Setup strobe blinking for speed led to indicate current speed
- strobe:
name: Speed Low
colors:
- state: true
brightness: 100%
duration: 150ms
- state: false
duration: 1000ms
- strobe:
name: Speed Medium
colors:
- state: true
brightness: 100%
duration: 150ms
- state: false
duration: 150ms
- state: true
brightness: 100%
duration: 150ms
- state: false
duration: 150ms
- strobe:
name: Speed High
colors:
- state: true
brightness: 100%
duration: 150ms
- state: false
duration: 150ms
- state: true
brightness: 100%
duration: 150ms
- state: false
duration: 150ms
- state: true
brightness: 100%
duration: 150ms
- state: false
duration: 1000ms
- platform: binary
name: "Fan Left LED - Temp & Hum Timer"
output: temp_hum_led
id: temp_hum_led_light
internal: true
restore_mode: ALWAYS_OFF
output:
- id: bluetooth_led
platform: gpio
pin: GPIO32
inverted: true
- id: speed_led
platform: gpio
pin: GPIO33
inverted: true
- id: temp_hum_led
platform: gpio
pin: GPIO25
inverted: true
- platform: template
id: fan_output_control
type: float
write_action:
- lambda: |-
auto call = id(speed_select).make_call();
std::string option = "";
if (state > 0.67) {
if (id(speed_select).state != "High")
option = "High";
} else if (id(three_speed_fan) && state > 0.34) {
if (id(speed_select).state != "Medium")
option = "Medium";
} else if (state > 0.1 && id(speed_select).state != "Low") {
option = "Low";
}
if (option != "") {
call.set_option(option);
call.perform();
}
fan:
- platform: speed
output: fan_output_control
name: "Fan"
id: attic_fan
speed_count: 2 # Change to 3 on a 3 speed fan
restore_mode: ALWAYS_OFF
on_turn_on:
- switch.turn_on: on_off_switch
on_turn_off:
- switch.turn_off: on_off_switch
switch:
- platform: gpio
pin: 5
name: "Fan Speed Low"
id: Speed_Low
restore_mode: ALWAYS_OFF
interlock: &interlock_group [Speed_Low, Speed_Medium, Speed_High] #Interlock prevents multiple on relays
interlock_wait_time: 2s #2 seconds of all relays off before enabling a new speed
internal: true
on_turn_on:
- light.turn_on:
id: speed_led_light
effect: "Speed Low"
on_turn_off:
- light.turn_off:
id: speed_led_light
# This switch does not function on 2 speed fans
- platform: gpio
pin: 22
name: "Fan Speed Medium"
id: Speed_Medium
restore_mode: ALWAYS_OFF
interlock: *interlock_group
interlock_wait_time: 2s
internal: true
on_turn_on:
- light.turn_on:
id: speed_led_light
effect: "Speed Medium"
on_turn_off:
- light.turn_off:
id: speed_led_light
- platform: gpio
pin: 23
name: "Fan Speed High"
id: Speed_High
restore_mode: ALWAYS_OFF
interlock: *interlock_group
interlock_wait_time: 2s
internal: true
on_turn_on:
- light.turn_on:
id: speed_led_light
effect: "Speed High"
on_turn_off:
- light.turn_off:
id: speed_led_light
- platform: template
name: "Fan On/Off"
id: on_off_switch
turn_on_action:
lambda: |-
if (id(speed_mode) == 1) {
id(Speed_Low).turn_on();
} else if (id(three_speed_fan) && id(speed_mode) == 2) {
id(Speed_Medium).turn_on();
} else {
id(Speed_High).turn_on();
}
if (id(timer_en)){ //start the timer if its set
id(timer_stop) = id(homeassistant_time).now().timestamp + id(timer_offset);
id(time_left).publish_state((double)id(timer_offset) / 60.0);
}
//turn on fan component if it's not already on and set it to the current speed mode
if (!id(attic_fan).state && !id(select_lock)) {
id(select_lock) = true;
auto call = id(attic_fan).turn_on();
call.set_speed(id(speed_mode));
call.perform();
}
turn_off_action:
lambda: |-
id(Speed_Low).turn_off();
id(Speed_Medium).turn_off();
id(Speed_High).turn_off();
id(timer_stop) = 0;
id(time_left).publish_state(0);
//turn off fan component if it's not already off
if(id(attic_fan).state) {
id(timer).publish_state("off");
auto call = id(attic_fan).turn_off();
call.perform();
}
lambda: |-
return (id(Speed_Low).state | id(Speed_Medium).state | id(Speed_High).state);
- platform: template
name: "Fan Auto Temp Control On/Off"
id: auto_temp_control
restore_mode: RESTORE_DEFAULT_OFF
optimistic: True
turn_on_action:
then:
- if:
condition:
lambda: "return id(attic_temperature).state >= id(high_speed_above_temp).state && id(attic_temperature).state < id(failsafe_temp);"
then:
- fan.turn_on:
id: attic_fan
speed: !lambda "return id(three_speed_fan) ? 3 : 2;"
- if:
condition:
lambda: "return id(three_speed_fan) && id(attic_temperature).state >= id(medium_speed_above_temp).state && id(attic_temperature).state < id(high_speed_above_temp).state && id(attic_temperature).state < id(failsafe_temp);"
then:
- fan.turn_on:
id: attic_fan
speed: 2
- if:
condition:
lambda: "return id(attic_temperature).state >= id(low_speed_above_temp).state && id(attic_temperature).state < (id(three_speed_fan) ? id(medium_speed_above_temp).state : id(high_speed_above_temp).state) && id(attic_temperature).state < id(failsafe_temp);"
then:
- fan.turn_on:
id: attic_fan
speed: 1
- if:
condition:
lambda: "return id(attic_temperature).state < id(low_speed_above_temp).state;"
then:
- fan.turn_off: attic_fan
i2c: #setup i2c for temp/humidity sensor
sda: 18
scl: 19
scan: true
id: bus_a
sensor:
- platform: wifi_signal
name: "Fan WiFi Strength"
update_interval: 60s
- platform: uptime
name: "Fan Uptime"
- platform: template
name: "Fan Time Left"
id: time_left
update_interval: 20s
unit_of_measurement: "minutes"
lambda: |-
if (id(timer_en)) {
auto time_remaining = id(timer_stop) - id(homeassistant_time).now().timestamp;
return (time_remaining >=0) ? ((float)time_remaining / 60.0):0;
} else if (id(time_left).state != 0) {
return 0;
}
return {};
- platform: sht3xd
temperature:
id: attic_temperature
device_class: temperature
state_class: measurement
name: "Temperature"
filters:
- lambda: return x * (9.0/5.0) + 32.0; #Change to Freedom Units
unit_of_measurement: "°F"
on_value_range:
# Fire Protection Automation
- above: !lambda return id(failsafe_temp);
then:
- fan.turn_off: attic_fan
# Automated High Speed Trigger
- above: !lambda return id(high_speed_above_temp).state;
below: !lambda return id(failsafe_temp);
then:
- if:
condition:
switch.is_on: auto_temp_control
then:
- fan.turn_on:
id: attic_fan
speed: !lambda "return id(three_speed_fan) ? 3 : 2;"
# Automated Medium/Low Speed Trigger
- above: !lambda "return id(three_speed_fan) ? id(medium_speed_above_temp).state : id(low_speed_above_temp).state;"
below: !lambda return id(high_speed_above_temp).state;
then:
- if:
condition:
switch.is_on: auto_temp_control
then:
- fan.turn_on:
id: attic_fan
speed: !lambda "return id(three_speed_fan) ? 2 : 1;"
# Automated Low Speed Trigger (only used for 3 speed fans)
- above: !lambda return id(low_speed_above_temp).state;
below: !lambda "return id(three_speed_fan) ? id(medium_speed_above_temp).state : id(low_speed_above_temp).state;"
then:
- if:
condition:
lambda: "return id(three_speed_fan) && id(auto_temp_control).state;"
then:
- fan.turn_on:
id: attic_fan
speed: 1
# Automated Off Trigger
- below: !lambda return id(off_below_temp).state;
then:
- if:
condition:
switch.is_on: auto_temp_control
then:
- fan.turn_off: attic_fan
humidity:
name: "Humidity"
address: 0x44
update_interval: 60s
text_sensor:
- platform: wifi_info
ip_address:
name: "Fan IP Address"