Push button to alternate PWM Fan + Auto off-timer - D1 Mini

Hi,

I have my first ESP Home project running fine now. Got a speedsensor and a PWM control.

My Noctua fan can be stoped completly via the PWM (not all fans supports that):

substitutions:
  hostid: fireplace_fan
  hostname: Fireplace Fan
  comment: Fireplace fan controller
  node_platform: ESP8266
  node_board: d1_mini

packages:
  core: !include common/core.yaml
  basic_sensors: !include common/basic_sensors.yaml

sensor:
  - platform: pulse_counter
    pin: 
      number: D2
      mode: INPUT_PULLUP
    unit_of_measurement: 'RPM'
    accuracy_decimals: 0 
    id: ${hostid}_speed
    name: ${hostname} Speed
    filters:
      - multiply: 0.5
    update_interval: 15s

fan:
  - platform: speed
    id: ${hostid}_fanid
    name: ${hostname}
    output: ${hostid}_fan_speed
    speed:
      low: 0.2
      medium: 0.5
      high: 1

output:
 - platform: esp8266_pwm
   pin: D3
   frequency: 25000 Hz
   id: ${hostid}_fan_speed

The next step for me is to attach a pushbutton. I want it to alternate the fan-modes (OFF, MIN, MEDIUM, MAX). So each press will result in moving to the next “speed”, when reaching MAX the next state shall be OFF.

How do I do it without involving HomeAssistant (I want it to work without network).

One more thing I have thought of. An auto-off timer since last fan state change (set via HA or by the push-button). Eg. set fan to OFF after 4 hours of “inactivity”.

Please advice me! :slight_smile:

1 Like

For the alternating state, create a global int variable. Create the button input as GPIO binary_sensor and give it a lambda automation on_on, where you increment the global int by one, except it hits 4, then reset it to 0. Each number represents a stet, eg. 0 means off, so use a switch statement at which number the variable is and execute the corresponding action. Also save the time of the button press in another global variable. When the saved time + like 4 hours equals the current time, set the state to 0.
Atleast thats the logic part, writing that as program is currently a bit hard, since im not in the mood for that. Im not sure about the timer part though. You can implement the increment things either in yaml (way more complicated and not my taste) or in c++ using the lambda, which i recommend for complicated things.

I just went back to this project…
Created I binary sensor, but how to get it to alternate the 4 states?

binary_sensor:
  - platform: gpio
    pin: 
      number: D1
      mode: INPUT_PULLUP
      inverted: True
    id: ${hostid}_button
    name: ${hostname} Button
    on_press:
      then:
        - logger.log: "Button clicked"
        - fan.turn_on: ${hostid}_fanid

Please advice!

globals:
  - id: ButtonCounter
    type: int
    restore_value: no
    initial_value: '0'
binary_sensor:
  - platform: gpio
    pin: 
      number: D1
      mode: INPUT_PULLUP
      inverted: True
    id: ${hostid}_button
    name: ${hostname} Button
    on_press:
      then:
        - lambda: !lambda |-
            if(id(ButtonCounter) == 3
            {
               id(ButtonCounter) = 0;
            }
              else
            {
               id(ButtonCounter)++
            }

that would be the function to increase a counter with each button press and resetting it when its value is 3. Now implement that if the counter is a specific value it switches to a specific speed setting.
For me, its now time to go to bed, so im gonna try to make something tomorrow or that weekend, but feel free to implement it by yourself. Oh, and dont forget, that you might have to work on the intendation of the code, i typed it in here not in a proper file editor.

Thanks!
I’ll tried this during my experiments… :slight_smile: Do I really need to have a global integer, as I can read the state of the current speed instead?

My code does compile, but does not work as intended…

substitutions:
  speed_low: '0.35'
  speed_medium: '0.60'
  speed_high: '1'

binary_sensor:
  - platform: gpio
    pin: 
      number: D1
      mode: INPUT_PULLUP
      inverted: True
    id: ${hostid}_button
    name: ${hostname} Button
    on_press:
      then:
        - lambda: |-
            if (id(${hostid}_fanid).speed == 0) {
              id(${hostid}_fanid).speed = FAN_SPEED_LOW;
            } else if (id(${hostid}_fanid).speed == ${speed_low}) {
              id(${hostid}_fanid).speed = FAN_SPEED_MEDIUM;
            } else if (id(${hostid}_fanid).speed == ${speed_medium}) {
              id(${hostid}_fanid).speed = FAN_SPEED_HIGH;
            } else if (id(${hostid}_fanid).speed == ${speed_high}) {
              id(${hostid}_fanid).speed = FAN_SPEED_LOW;
            }

But setting this won’t compile: id(${hostid}_fanid).speed = 0;

auto call = id(myfan).make_call();
call.set_speed(FAN_SPEED_MEDIUM);
call.perform();

Also, when you’re comparing with .speed, you need to use the same constants (e.g. FAN_SPEED_MEDIUM). I don’t know where you got those other numbers from.

Thanks,

I gave it a try yesterday… I was able to go from Low to Medium to High, but have trouble with setting it to off and get the condition off. Please see my code:

binary_sensor:
  - platform: gpio
    pin: 
      number: D1
      mode: INPUT_PULLUP
      inverted: True
    id: ${hostid}_button
    name: ${hostname} Button
    on_press:
      then:
        - lambda: |-
            auto call = id(${hostid}_fanid).make_call();
            if ( /* HOW TO CHECK IF FAN ARE OFF???*/ ) {
              call.set_speed(FAN_SPEED_LOW);
              call.perform();
              id(${hostid}_fanid).speed = FAN_SPEED_LOW;
            } else if (id(${hostid}_fanid).speed == FAN_SPEED_LOW) {
              call.set_speed(FAN_SPEED_MEDIUM);
              call.perform();
            } else if (id(${hostid}_fanid).speed == FAN_SPEED_MEDIUM) {
              call.set_speed(FAN_SPEED_HIGH);
              call.perform();
            } else if (id(${hostid}_fanid).speed == FAN_SPEED_HIGH) {
              call.set(/* HOW TO SET FAN TO OFF???*/ );
              call.perform();
            }

I can make it by using a counter, but will try without if I can… :slight_smile:

call.set_state(false);
You could also put the call.perform(); outside the if instead of in every case.
And take out the speed assignment in the first case.
To check if it’s off, use !id(${hostid}_fanid).state

Thanks!

I changed to:

binary_sensor:
  - platform: gpio
    pin: 
      number: D1
      mode: INPUT_PULLUP
      inverted: True
    id: ${hostid}_button
    name: ${hostname} Button
    on_press:
      then:
        - lambda: |-
            auto call = id(${hostid}_fanid).make_call();
            if (!id(${hostid}_fanid).state) {
              call.set_state(true);
              call.set_speed(FAN_SPEED_LOW);
            } else if (id(${hostid}_fanid).speed == FAN_SPEED_LOW) {
              call.set_speed(FAN_SPEED_MEDIUM);
            } else if (id(${hostid}_fanid).speed == FAN_SPEED_MEDIUM) {
              call.set_speed(FAN_SPEED_HIGH);
            } else if (id(${hostid}_fanid).speed == FAN_SPEED_HIGH) {
              call.set_state(false);
            }
            call.perform();

This gives me the correct hardware behavior!

But in the HA, when reaching the state OFF, the UI shows Fanspeed High, and the switch are off. Is there a way to set the input select to the OFF-state?

image

Tried to change the last if-statement to:

} else if (id(${hostid}_fanid).speed == FAN_SPEED_HIGH) {
     call.set_state(false);
     call.set_speed(FAN_SPEED_OFF);
}

But there are no FAN_SPEED_OFF… :scream:

A work around are to set it to FAN_SPEED_LOW, then it will go to the LOW-speed in the UI, when clicking the toggle-button in HA.

} else if (id(${hostid}_fanid).speed == FAN_SPEED_HIGH) {
    call.set_state(false);
    call.set_speed(FAN_SPEED_LOW);
}