Need help automating a dumb fan while also still using its physical remote

I’ve been banging my head against this for 2 weeks.

I want to use my fan’s physical remote and Home Assistant in tandem - and have HA stay in sync.

I’m using an IR blaster to send the commands, and the fan is plugged into an energy monitoring socket.

In my head it goes: Set “Speed 2” → Get current power usage → Determine current speed based on power → Send appropriate commands.

The complication is when the fan is “Off” or in “Silent Night” mode. I need to know what the fan’s speed/power use was before entering these states, because the fan returns to its previous speed or mode when leaving these two states.

If the fan is currently “Off” and I want “Speed 2” I need to know what speed it will be when it turns on, to send the appropriate commands. More details:


Fan: Rowenta Turbo Silence Extreme

Speeds: Off, 1, 2, 3,

Preset mode: Silent Night

Remote buttons: Power toggle, Increase speed, “Silent Night” toggle

Behavior: One button controls the speed. It increases the speed by 1, and after Speed 3 it returns to Speed 1 eg. 1 → 2 → 3 → 1 → 2, etc.

One button turns the fan on and off. When the fan is turned on, it returns to the speed or mode it was on before it turned off.

“Silent Night” is a low speed mode with a separate button. When pressed it enters “Silent Night” mode. When the fan is in “Silent Night” mode and the “Silent Night” button is pressed, the fan returns to the speed it was on before entering “Silent Night”

HOWEVER, if the fan is in “Silent Night” mode and the “Increase Speed” button is pressed, the fan will always go to Speed 2 - regardless of its previous speed.


Off: - 0W

Silent Night: 11 - 14W

Speed 1: 45 - 48W

Speed 2: 58 - 61W

Speed 3: 67 - 70W


Since I want to continue using the remote, I hope that the fan’s power usage is a good way to keep it all in sync.

I’ve been using Node-RED to try and piece this together - but if there’s a better way please tell me.

Thanks for reading.

Well, there’s your solution.
Send the button press to the IR blaster until there is no power going to the fan. You know its’ state then = off. Then IR blast to the setting you want.

If I’m understanding correctly, this won’t work.

The fan is toggled on and off by one button. When the fan is toggled on, it returns to the speed or mode it was in before it turned off.

For example if the fan is on “Speed 2”, then is toggled “Off”, then is toggled “On” - it will be on “Speed 2” when it starts.

This is why I need to know its state before it is toggled off. In the example, if I want to set the speed to “Speed 1” I would need to send the “power_toggle” command, then “increase_speed” command twice to get to “Speed 1”.

Thanks for replying!

I explained how I automated an IR controlled kitchen exhaust fan here:

Whether I control it via its front panel, its IR remote, via Alexa, or via the UI in Home Assistant, its current state is correctly displayed in Home Assistant.

Perhaps it can help you automate your fan.

That’s what I said. Start from a known point. Toggle it until there is no current draw. You then know its’ state and how many toggles it takes to get to your desired speed.

If I have used the remote that came with the fan to set the fan’s speed to “Speed 1” then to “Off”, Home Assistant knows that the fan is currently “Off”, but doesn’t know that when the fan powers on it will be on “Speed 1”.

Using the fan’s physical remote bypasses HA completely. That’s why I would like to know if there’s a way to store the fan’s power usage before it turned off - even if I have used the physical remote.

You can store the speed setting in an helper of some kind, update this value only if the fan is not off or in night mode, and then start from there.

Turn it on. Read the watts to get the current state.

It will if you follow jeffcrum’s advice.

In the link I posted above, I use a Trigger-based Template Sensor to report the fan’s speed based on its power consumption. It converts the fan’s power values into a fan speed value between 0 and 100 so that it can be used in a Template Fan.


EDIT

Here’s the relevant section of the Trigger-based Template Sensor that I mentioned:

template:
  - trigger:
      - platform: state
        entity_id: sensor.range_hood_energy_power
    sensor:
      - name: Range Hood Fan Speed
        unique_id: range_hood_fan_speed
        state: >
          {% set p = trigger.to_state.state | int(0) %}
          {{  { p < 125: 0,
                125 <= p < 160: 25,
                160 <= p < 185: 50,
                185 <= p < 225: 75,
                p > 225: 100 
              }.get(true, 0) }}

Obviously yours will be different but the key point is that the power ranges in the template should have no gaps (i.e. in your first post there are gaps between 0 and 11, 14 and 45, 48 and 58, 61 and 67).

So, when the fan turns on, it turns on to the last “on” setting. I have a desk light that works that way. So annoying…

@jeffcrum has the solution. Measure the power when the fan is on in all configurations, then in an automation, you can use the known “on” levels to do whatever you want. Just toggle until you get the power draw that matches your target speed

I was in similar situation 2 weeks back. I got it working with custom:mushroom-number-card for slider and type: custom:button-card with Animation where the fan will be spinning with variable speeds. I managed to read the status of the command pressed via Physical Remote/Home Assistant Slider and saving that in a helper file. There is an automation job triggered based on the Fan entity status change. The Fan will resume to previous speed using the helper stored value. If you are interested I can share the yaml files.

FanSpeed_Animation

This was the solution. I have it working in node-RED now, but it’s very messy (because I’m still new to HA). Now I’ll work on cleaning it up, or switching to YAML if it’s more efficient.

Aha! I tried to use your template, and put in my own values. But it didn’t work. The issue was the gaps. Thank you.

Yes please! I’ll take any help I can get.

You can increase each range by about half the gap value (i.e. split the difference).

For example:

p < 12: 0,
12 <= p < 46: 25,
46 <= p < 59: 50,
p > 59: 100

Or:

p < 12: 0,
12 <= p < 46: 33,
46 <= p < 59: 66,
p > 59: 100

Is this a RF based remote or IR based? I am using IR based remote to control the speed of the Fan. If you have any HA integrated IR blaster then the solution I have mentioned will work, otherwise it may not work for you. In my case I am using physical remote as well to control the speed but the IR blaster will receive the signal and update HA about the speed it has received from physical remote. That way I am able to sync the speed.

It’s an IR based remote.

I’m using this blaster: https://www.aliexpress.com/item/1005006740533109.html

And I have it set up using the Tuya Local integration from HACS

If my blaster is capable of this, I would prefer this method. My energy monitoring socket can sometimes take ~15 seconds to update its power usage.

Great news. I am using similar Tuya based IR remote, however it is based on Beken Chipset, used ‘libretiny’ to make it cloud free. I can control the Fan Speed using Alexa Voice Commands as well. You may try similar approach to make it cloudfree if you wish and your device supports that.

Note: There may be better way of doing this automation, I am still learning my yaml and automation skills. Since this method is working for me, I am sharing it here. I am all hands for any best approach with shorter and neater options.

We require 3 components.

  1. IR blaster which can read IR signals and Transmits Signals, integrated with Home Assistant.
  2. A helper entity that can hold the Fan Speeds
  3. An Automation job that will be triggered by IR Transmitter or from HA Dashboards.

My IR blaster code:

light:
- platform: status_led
  name: LED
  icon: mdi:led-off
  id: status_light
  pin:
    number: P8 #Blinking
    inverted: False
  restore_mode: RESTORE_DEFAULT_OFF  

remote_receiver:
- id: IR_RX  
  pin:
    number: P7 #Working 
    inverted: true
    mode: INPUT_PULLUP
  dump:
      - nec        

remote_transmitter:
  pin: P26 # Working
  id: IR_TX
  carrier_duty_percent: 50%

sensor:
  - platform: wifi_signal
    name: "WiFi Signal"
    id: wifi_signal_db
    device_class: "signal_strength"
    update_interval: 5min

  - platform: copy
    source_id: wifi_signal_db
    name: "WiFi Signal Strength"
    filters:
      - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
    unit_of_measurement: "%"
    entity_category: "diagnostic"
    device_class: "signal_strength"

  - platform: uptime
    name: "Uptime"
    update_interval: 1h

text_sensor:
  - platform: wifi_info
    ip_address:
      name: IP Address
      icon: mdi:wifi
    ssid:
      name: SSID
      icon: mdi:wifi
    mac_address:
      name: MAC
      icon: mdi:wifi

  - platform: template
    name: Learned Command
    icon: mdi:remote
    id: learned_command

  - platform: template
    name: "Fan Speed at"
    id: hallfan_template_speed
    icon: mdi:speedometer

  - platform: template
    name: "Fan Remote Status"
    id: hall_fan_rmt_status
    icon: mdi:switch
    internal: True        

binary_sensor:
  - platform: remote_receiver
    id: remo_fan_off
    nec:
        address: 0xF300
        command: 0x6E91
    on_press:
      then:
        - switch.turn_on: Hall_On_Off
        - text_sensor.template.publish:
            id: hall_fan_rmt_status
            state: !lambda 'return "Off";'       

  - platform: remote_receiver
    id: remo_speed1
    nec:
      address: 0xF300
      command: 0x748B
    on_press:
      then:
        - switch.turn_on: Speed1
        - text_sensor.template.publish:
            id: hallfan_template_speed
            state: !lambda 'return "1";'         

  - platform: remote_receiver
    id: remo_speed2
    nec:
      address: 0xF300
      command: 0x6F90
    on_press:
      then:
        - switch.turn_on: Speed2
        - text_sensor.template.publish:
            id: hallfan_template_speed
            state: !lambda 'return "2";'        
        
  - platform: remote_receiver
    id: remo_speed3
    nec:
      address: 0xF300
      command: 0x758A
    on_press:
      then:
        - switch.turn_on: Speed3
        - text_sensor.template.publish:
            id: hallfan_template_speed
            state: !lambda 'return "3";'        

  - platform: remote_receiver
    id: remo_speed4
    nec:
      address: 0xF300
      command: 0x6C93
    on_press:
      then:
        - switch.turn_on: Speed4
        - text_sensor.template.publish:
            id: hallfan_template_speed
            state: !lambda 'return "4";'        

  - platform: remote_receiver
    id: remo_speed5
    nec:
      address: 0xF300
      command: 0x7788
    on_press:
      then:
        - switch.turn_on: Speed5   
        - text_sensor.template.publish:
            id: hallfan_template_speed
            state: !lambda 'return "5";'

  - platform: remote_receiver
    id: remo_boost
    nec:
      address: 0xF300
      command: 0x708F
    on_press:
      then:
        - switch.turn_on: Speed5   
        - text_sensor.template.publish:
            id: hallfan_template_speed
            state: !lambda 'return "6";'                            

switch:
# Home Assistant controlled switch - to enable "learning mode"
  - platform: template
    name: Command Received
    id: remote_command_rx
    icon: mdi:remote
    optimistic: true

  - platform: template
    name: "Hall Fan Off/On" #NEC
    id: Hall_On_Off
    turn_on_action:
      - light.turn_on: status_light       
      - remote_transmitter.transmit_nec:
          address: 0xF300
          command: 0x6E91
      - delay: 60ms          
      - light.turn_off: status_light
      - switch.turn_off: Hall_On_Off               

  - platform: template
    name: "Speed 1" #NEC
    id: Speed1
    turn_on_action:
      - light.turn_on: status_light      
      - remote_transmitter.transmit_nec:
          address: 0xF300
          command: 0x748B
      - delay: 60ms
      - light.turn_off: status_light    
      - text_sensor.template.publish:
          id: hallfan_template_speed
          state: !lambda 'return "1";'        
      - switch.turn_off: Speed1      

  - platform: template
    name: "Speed 2" #NEC
    id: Speed2
    turn_on_action:
      - light.turn_on: status_light        
      - remote_transmitter.transmit_nec:
          address: 0xF300
          command: 0x6F90
      - delay: 60ms
      - light.turn_off: status_light
      - text_sensor.template.publish:
          id: hallfan_template_speed
          state: !lambda 'return "2";'        
      - switch.turn_off: Speed2         

  - platform: template
    name: "Speed 3" #NEC
    id: Speed3
    turn_on_action:
      - light.turn_on: status_light        
      - remote_transmitter.transmit_nec:
          address: 0xF300
          command: 0x758A
      - delay: 60ms
      - light.turn_off: status_light
      - text_sensor.template.publish:
          id: hallfan_template_speed
          state: !lambda 'return "3";'        
      - switch.turn_off: Speed3            

  - platform: template
    name: "Speed 4" #NEC
    id: Speed4
    turn_on_action:
      - light.turn_on: status_light  
      - remote_transmitter.transmit_nec:
          address: 0xF300
          command: 0x6C93         
      - delay: 60ms
      - light.turn_off: status_light
      - text_sensor.template.publish:
          id: hallfan_template_speed
          state: !lambda 'return "4";'           
      - switch.turn_off: Speed4             

  - platform: template
    name: "Speed 5" #NEC
    id: Speed5
    turn_on_action:
      - light.turn_on: status_light       
      - remote_transmitter.transmit_nec:
          address: 0xF300
          command: 0x7788         
      - delay: 60ms
      - light.turn_off: status_light
      - text_sensor.template.publish:
          id: hallfan_template_speed
          state: !lambda 'return "5";'         
      - switch.turn_off: Speed5

  - platform: template
    name: "Boost" #NEC
    id: Boost
    turn_on_action:
      - light.turn_on: status_light        
      - remote_transmitter.transmit_nec:
          address: 0xF300
          command: 0x708F
      - delay: 60ms
      - light.turn_off: status_light      
      - switch.turn_off: Boost  

My Helper Settings:

My Automation Yaml:

alias: Hall Fan Speed from Input
description: ""
trigger:
  - platform: state
    entity_id:
      - input_number.hall_fan_speed
    from: null
    to: null
  - platform: state
    entity_id:
      - switch.hall_fan
    from: "off"
    to: "on"
condition:
  - condition: device
    type: is_on
    device_id: b8d490dde7c6399dc64d2e99db6a71a1
    entity_id: 72e0d211de705f266da55e7c4fb17cde
    domain: switch
action:
  - choose:
      - conditions:
          - condition: template
            value_template: "{{ states('input_number.hall_fan_speed') | float == 1}}"
        sequence:
          - service: switch.turn_on
            data: {}
            target:
              entity_id: switch.ir_blaster_hallfan_speed_1
      - conditions:
          - condition: template
            value_template: "{{ states('input_number.hall_fan_speed') | float == 2}}"
        sequence:
          - service: switch.turn_on
            target:
              entity_id:
                - switch.ir_blaster_hallfan_speed_2
            data: {}
      - conditions:
          - condition: template
            value_template: "{{ states('input_number.hall_fan_speed') | float == 3}}"
        sequence:
          - service: switch.turn_on
            data: {}
            target:
              entity_id: switch.ir_blaster_hallfan_speed_3
      - conditions:
          - condition: template
            value_template: "{{ states('input_number.hall_fan_speed') | float == 4}}"
        sequence:
          - service: switch.turn_on
            data: {}
            target:
              entity_id: switch.ir_blaster_hallfan_speed_4
      - conditions:
          - condition: template
            value_template: "{{ states('input_number.hall_fan_speed') | float == 5}}"
        sequence:
          - service: switch.turn_on
            data: {}
            target:
              entity_id: switch.ir_blaster_hallfan_speed_5
      - conditions:
          - condition: template
            value_template: "{{ states('input_number.hall_fan_speed') | float == 6}}"
        sequence:
          - service: switch.turn_on
            target:
              entity_id: switch.ir_blaster_hallfan_boost
            data: {}
mode: single

My Fan On/Off is controlled by Smart Switch most of the time. I have shared the IR codes that controls the On/Off which may be used if required. Your Fan Remote’s IR protocol and codes may be different, the codes may not work as it is.I hope you may get the idea of what I have implemented.

There is always another rabbit hole to dive into haha. My IR blaster is compatible - it uses the BK7231T MCU and the pins are documented. So I’ll spend a while toying with this.

Thanks for sharing your code. I’m sure I’ll be using a large chunk of it.

Good to know your is running BK7231T.
You may use below given board type in your yaml based on the actual chip. I have found same model of IR blaster, from same vendor using 2 different models of chips which I bought in separate shipments. The pinouts are slightly different. I am running BK7231T and BK7231N chip based IR blasters, will be able to help you with the pins that I have configured with. Make a note of the chip once you connect to the device first time before pushing your yaml into it.

bk72xx:
  board: generic-bk7231n-qfn32-tuya

or

bk72xx:
 board: generic-bk7231t-qfn32-tuya

There will be another rabit hole that you will be finding after flashibng your device with libretiny. The IR protocols. Let me know if you require any help after you move your device with Libretiny firmware. if it’s NEC then its more reliable and easier. Good Luck :slight_smile:

I’m back! I’ve been on several detours. But I’ve (mostly) sorted it out.

I didn’t end up using your specific blaster or automation code, because my fan works very differently eg. there’s one button to control the speed, and it only increases the speed, looping continuously 1 → 2 → 3 → 1 → 2 etc.

But your code was very useful for the general concepts I needed, so thank you.

You were completely right about IR protocols being another rabbit hole. The remote sends Pronto and interestingly RC Switch Raw. It also has slight variations in signals that it can send for each button - depending on the length of button press.

When I tried using pronto as the receiver dump it was too inconsistent at recognizing them, 70% accuracy at most (which I don’t understand, because when I put the received pronto into IrScrutinizer the signals were consistent)

But using RC Switch Raw yields higher consistency ~95%. Which still isn’t the preferred 100%.

Until I can figure out these signals, I’m also using my energy monitoring socket to keep the blaster and fan in sync.

Thanks again!