After some time experimenting and then walking away and then trying again I have a solution.
I wanted a singular push button to toggle between states, however I was struggling with Lamda, so instead of each press moving to the next setting, I decided to use multi_click. A long press turns off the fan, and one to three quick presses turning it on and into the given fan mode. As I mostly use High, I went High, Med, Low for 1, 2, and 3 clicks respectively. See the code below.
esphome:
name: box_fan_one
platform: ESP8266
board: d1_mini
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_pass
manual_ip:
static_ip: x.x.x.x
gateway: x.x.x.x
subnet: 255.255.255.0
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Box Fan Fallback Hotspot"
password: !secret ap_fallback_pass
captive_portal:
# Enable logging
logger:
# Enable Home Assistant API
api:
password: !secret api_pass
ota:
password: !secret ota_pass
substitutions: #for the name of the fan to input below
name: box_fan_one
binary_sensor:
- platform: gpio # For push button onboard
id: push_button
pin:
number: D2
mode: INPUT_PULLUP
on_multi_click:
- timing:
- ON for 1s to 3s
- OFF for at least 0.2s
then:
- logger.log: "Long Press"
- switch.turn_off: relay1
- switch.turn_off: relay2
- switch.turn_off: relay3
- timing:
- ON for at most 1s
- OFF for at least 0.2s
then:
- logger.log: "Single Press"
- switch.turn_on: relay1
- timing:
- ON for at most 1s
- OFF for at most 1s
- ON for at most 1s
- OFF for at least 0.2s
then:
- logger.log: "Double Press"
- switch.turn_on: relay2
- timing:
- ON for at most 1s
- OFF for at most 1s
- ON for at most 1s
- OFF for at most 1s
- ON for at most 1s
- OFF for at least 0.2s
then:
- logger.log: "Triple Press"
- switch.turn_on: relay3
switch:
- platform: gpio
name: "${name}_High"
pin:
number: D5
inverted: True
icon: mdi:fan-speed-3
id: relay1
interlock: &interlock_group [relay1, relay2, relay3]
interlock_wait_time: 300ms
- platform: gpio
name: "${name}_Medium"
pin:
number: D6
inverted: True
icon: mdi:fan-speed-2
id: relay2
interlock: *interlock_group
interlock_wait_time: 300ms
- platform: gpio
name: "${name}_Low"
pin:
number: D7
inverted: True
icon: mdi:fan-speed-1
id: relay3
interlock: *interlock_group
interlock_wait_time: 300ms
This gave me the physical control I desired.
Next step is understanding the fan template to get this to be usable as a fan rather than three switch devices.
I hope this helps someone, if anyone has questions or improvements let me know!
Never was able to do it. Its theoretically possible to do using templating. I just have each of the fan speeds available on a single row on the dashboards that I need it. I primarily use button stations around the apartment so I didn’t end up needing to fuss with it as the 3 interlocking switches works fine.
This is spectacular, ty for sharing your code. I was able to incorporate it into my existing code to have a proper fan entity in HA. MUCH better than the three switches I had before. I also tweaked the code for the onboard button I have, now rather than triggering relays it uses the fan entity so it feeds back to HA properly. LMK if you see any glaring issues in the code but I am pleased to report that I think that this is (for now) the final stage of this project.
I don’t personally use the status sensor but I do have the RSSI and uptime (converted to human readable, don’t recall where I found that so I cant credit anyone) included as well.
esphome:
name: boxfanone
platform: ESP8266
board: d1_mini
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_pass
manual_ip:
static_ip: x.x.x.x
gateway: x.x.x.x
subnet: 255.255.0.0
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Box Fan Fallback Hotspot"
password: !secret ap_fallback_pass
captive_portal:
# Enable logging
logger:
# Enable Home Assistant API
api:
password: !secret api_pass
ota:
password: !secret ota_pass
substitutions: #for the name of the fan to input below
name: Bedroom Box Fan
binary_sensor:
- platform: gpio # For push button onboard
id: push_button
pin:
number: D2
mode: INPUT_PULLUP
on_multi_click:
- timing:
- ON for 1s to 3s
- OFF for at least 0.2s
then:
- logger.log: "Long Press"
- fan.turn_off:
id: bedroomboxfan
- timing:
- ON for at most 1s
- OFF for at least 0.2s
then:
- logger.log: "Single Press"
- fan.turn_on:
id: bedroomboxfan
speed: 3
- timing:
- ON for at most 1s
- OFF for at most 1s
- ON for at most 1s
- OFF for at least 0.2s
then:
- logger.log: "Double Press"
- fan.turn_on:
id: bedroomboxfan
speed: 2
- timing:
- ON for at most 1s
- OFF for at most 1s
- ON for at most 1s
- OFF for at most 1s
- ON for at most 1s
- OFF for at least 0.2s
then:
- logger.log: "Triple Press"
- fan.turn_on:
id: bedroomboxfan
speed: 1
switch:
- platform: gpio
internal: true
name: "${name} High Relay"
pin:
number: D5
inverted: True
icon: mdi:fan-speed-3
id: relay3
interlock: &interlock_group [relay1, relay2, relay3]
interlock_wait_time: 300ms
- platform: gpio
name: "${name} Medium Relay"
internal: true
pin:
number: D6
inverted: True
icon: mdi:fan-speed-2
id: relay2
interlock: *interlock_group
interlock_wait_time: 300ms
- platform: gpio
name: "${name} Low Relay"
internal: true
pin:
number: D7
inverted: True
icon: mdi:fan-speed-1
id: relay1
interlock: *interlock_group
interlock_wait_time: 300ms
- platform: restart
name: "$name Restart"
output:
- platform: template
id: fan_1
type: float
write_action:
- if:
condition:
lambda: return ((state == 0));
then:
# action for off
- switch.turn_off: relay1
- switch.turn_off: relay2
- switch.turn_off: relay3
- if:
condition:
lambda: return ((state > 0) && (state < 0.4));
then:
# action for speed 1
- switch.turn_off: relay2
- switch.turn_off: relay3
- switch.turn_on: relay1
- if:
condition:
lambda: return ((state > 0.4) && (state < 0.7));
then:
# action for speed 2
- switch.turn_off: relay1
- switch.turn_off: relay3
- switch.turn_on: relay2
- if:
condition:
lambda: return ((state > 0.7));
then:
# action for speed 3
- switch.turn_off: relay1
- switch.turn_off: relay2
- switch.turn_on: relay3
fan:
- platform: speed
id: bedroomboxfan
output: fan_1
name: "Bedroom Box Fan"
speed_count: 3
text_sensor: #Human Readable Uptime
- platform: template
name: $name Uptime
id: uptime_human
icon: mdi:clock-start
sensor:
- platform: wifi_signal
name: "$name WiFi Signal"
update_interval: 60s
- platform: uptime #Uptime in Seconds
name: $name Uptime Sensor
id: uptime_sensor
update_interval: 60s
internal: True
on_raw_value:
then:
- text_sensor.template.publish:
id: uptime_human
state: !lambda |-
int seconds = round(id(uptime_sensor).raw_state);
int days = seconds / (24 * 3600);
seconds = seconds % (24 * 3600);
int hours = seconds / 3600;
seconds = seconds % 3600;
int minutes = seconds / 60;
seconds = seconds % 60;
return (
(days ? String(days) + "d " : "") +
(hours ? String(hours) + "h " : "") +
(minutes ? String(minutes) + "m " : "") +
(String(seconds) + "s")
).c_str();
Just a follow up on this fan project of mine, I have the fan working perfectly in HA but I really want to be able to use the 3 speed settings as per the old HA fan usage, not the stupid % based method.
Currently my fan seems to show both in HA, but I can only set it via %. As you can see below, the descriptive fan speeds are listed under ‘attributes’, but I can only seem to set it via %. I realise the fan speeds (descriptive) was deprecated (stupid decission)… but…
Is there any way to force the 3 speeds to be accessible as low, medium and high? (I have this for another fan running Tasmota and integrated over MQTT)
Thanks. I actually got it sorted out as described in my post above. The only thing I can’t figure out is how to select the fan speeds via the description (ie: off, low, med, high) in HA. Currently I have to use a slider to select the percentage steps. I have another fan which can be selected by descriptor but that’s running Tasmota, not ESPhome so this issue is taken care of in the HA MQTT fan code.
I am really curious if you built this out just because it was an interest project? There are a handful of devices across multiple protocols that already do this in a compact little unit that you shove into the fan wiring already. Don’t take my question as criticism, just curious.
At the time I didn’t really know about the solutions, and I found a project similar to this so I just decided to try and do it myself, really didn’t think about it past that. I’m glad I did though, learned a lot about ESPHome from this project.
Edit: Most everything I build for my smarthome is 1 part functionality and 2 parts, I wonder if I can do X.
The reasoning was because the fan I’m controlling is too powerful for the off-the-shelf devices. Unfortunately I found this out the hard way because I didn’t realise the fan controller I had wouldn’t handle my fan… it let the magic smoke out.
This fan controller I made can handle much larger / higher power fans.
Thanks for updating your code with the fan settings. This is working perfectly for me with a three relay board that I’ll put in a pedestal fan.
Can I ask as question - for the button, how did you wire that up? Do you also pull it low/high with a resistor to something as I’m seeing the button go on/off in the logs when I’m not touching anything.
Its been a long while since I have had the box open. You should be able wire it with a resistor to stop it from floating like that. Any button wiring you like should work.
Is there no other way to represent three states with one key? I have the same problem and find the solution on_multi_click not very attractive. I switch the fan via solid state relays but that doesn’t really matter. I am looking for a simple solution for automation…
Press button level 1
press button again level 2
press button again level 3
button again fan off
And then again from the beginning. I can’t find a solution myself.
Sorry, but I can only communicate in English through the translator. I would like to achieve the automation only via ESPHome. Everything else makes no sense for a fan. Would like to switch the part even if there is no network. Would be great if you had a solution for me.
If would need to be a condition based action, based on what fan speed is currently selected. In ESPhome you can do if/then/else statements, but I’m not sure if you can do if/elseif/elseif/else or not…
There is likely a way to do this with lambda’s but I’m not skilled in writing those.
ESPhome code: (not complete)
</s> <s>button:</s> <s> - platform: gpio</s> <s> name: Fan Button</s> <s> id: button</s> <s> on_press:</s> <s> then:</s> <s> - if:</s> <s> condition:</s> <s> - switch.is_on: switch_low</s> <s> then:</s> <s> - switch.turn_off: switch_low</s> <s> - switch.turn_on: switch_med</s> <s> Hopefully you get the idea, but I would imagine there is a more elegant way to do this with lambda’s.
Thanks for the quick reply. Unfortunately, I do not quite understand the solution. I have a button and would like to achieve a different state with each press on it. So the speed Slow Medium and Fast or Off. In C++ this would probably be a counter from 0 to 3 and the assignment via a switch-case statement. This should be possible with ESPHome! Only I do not know how…