šŸ’Ø Adaptive fan speed control based on temperature and speed range

Sure thing. I have a utility/tech room with a heat pump, solar inverter, water boiler and a computer rack. As you can imagine the room gets quite hot, especially during the day since the solar inverter produces a lot of heat. I have installed a ventilation fan, controlled by 0-10V signal. The fan is now able to reduce the temperature to keep the room cold enough. Without it, it would be a sauna :smiley:
See how the fan speed adjusts during the day.

1 Like

This is such a fantastic blueprint. Iā€™m curious if thereā€™s a way to implement a manual override? Iā€™m thinking of the Adaptive Lighting integration in HACS, where if you manually adjust the brightness of a light, Adaptive Lighting wonā€™t mess with it until it turns off and back on again.

1 Like

Awesome to hear youā€™ve found it useful! Yeah Iā€™ve been wondering myself about the manual override. Iā€™m just not sure how best to trigger it (or identify that the fan has been manually controlled).

Methods I can think of so far (that would be a hack at best) are:

  1. Add an Input Boolean: Create an input boolean to act as a flag indicating whether a manual override is in effect.
  2. Detect manual changes: detect manual overrides automatically without needing to toggle an input boolean manually.

The option is easy. The second option is harder.
Happy to have any suggestions from the community as to how this should be done?

I used scripts for manually overriding my cooling fan. I can turn the blueprint off for one hour or set it to manual speed 1-5 for one hour and then activating the BP automation. Auto reactivates the BP automation

Works great, just as the blueprint itself. Now Iā€™m just trying to work out how to set the desired temperature in the blueprint from my dashboard using an input number helper instead of having to edit the blueprint each time.

Still trying to figure out the best way to implement this. Just so I have it right in my head: your desire would be to have an input_helper that allows you to select a preferred temperature and then have min and max temps set using offsets in the blueprint?

Iā€™m thinking that this might be best implemented in a seperate spin-off of the current blueprint for those wanting this feature. Iā€™ll have a play with it and get back to you :slight_smile:

Exactly! Iā€™m thinking like you set the desired temperature on your AC unit with your remote. Sometimes you want a bit warmer and sometimes a bit cooler.

The min/max value would be set via the offset. Those youā€™d just have to set once as the ā€œtemperature curveā€ would then move up and down based on the set temperature.

Sir_Goodenough has a working input number in his BP if that would help

https://community.home-assistant.io/t/auto-fan-temp-control-for-3-speed-fan-using-ha-fan-or-mqtt-integration/326419

Would you be able to add to the blueprint an option to invert the blocking entity so that it would work with an entity that is off. My use case is a whole house fan that I want to run, but only when the window is open, which in this case it goes from an off state to on state.

Also it would be nice to have a separate enabling entity you can chose. Idea being I could install a remote switch that starts of the automation, otherwise the automation is disabled.

If youā€™re not aware a Blueprint can be converted to a regular Automation, then you could make all your customisations.

Hi @adamalli7, sorry for my late reply and thanks for your interest and ideas. Regarding your request to add an option to invert the blocking entity, I think the cost for implementing this in terms of the logic needed and the added complexity, proabably means I wonā€™t add this feature. A much easier way would be to create your own template sensor that inverts the state of your entity. Hereā€™s an example of how to to that:

  1. Open your Home Assistant configuration file: This is usually located at /config/configuration.yaml.
  2. Add the template sensor configuration: In your configuration.yaml file, add the following lines to define your template sensor. Replace sensor.your_entity with the actual entity ID you want to invert.
template:
  - sensor:
      - name: "Inverted Entity State"
        state: >
          {% if is_state('sensor.your_entity', 'on') %}
            off
          {% else %}
            on
          {% endif %}
        availability: >
          {{ states('sensor.your_entity') not in ['unknown', 'unavailable'] }}

Hope this helps!

@cappadanna I havnā€™t forgotten about you! I just need to find the time to implement it :joy:

I just changed the blueprint to do what I needed it to do. Simple as changing the entity value from ā€œonā€ to ā€œoffā€

1 Like

No worries, I know just how it is :joy:

1 Like

Hey,
I really like your blueprint the only thing that is missing is a maximum percentage change. I got some wild jumps on my air conditioning fan. :joy:
Could you implant this value as well?
Thanks!

Haha interesting! Was that caused by a large jump in temperature though? Whatā€™s your thoughts on what should happen in this case? Eg: should the blueprint set the fan to the maximum allowed step change in fan speed and then continue to step it up until it eventually reached the required fan speed for the current temperature?

@Creepe95 ok Iā€™ve added a maximum percentage change slider. Give it a go and let me know how it goes :slight_smile:

Update the blueprint by re-importing it. It is backwards compatible, so your old settings will be preseved if you have any automations that are already using this blueprint.

Notes on this change:
To ensure the set_fan_speed value never increases or decreases by more than a desired threshold, you can set a max_change which is a threshold value set for the difference betwen the current fan_speed and the calculated set_fan_speed. If the difference exceeds max_change, the set_fan_speed will be adjusted to fan_speed + max_change (for the positive direction) or fan_speed - max_change (for the negative direction) as necessary.

IMPORTANT!
Because the automation is triggered only when the temperature sensor changes or when the fan is turned on, if you have set a small max_change and the time between temperature changes is large, the time it takes for the fan to get the set_fan_speed could be large.

ā€“edit-- nvm, I think the script below works.

Awesome blueprint!
Iā€™ve tried to add the option to select another temperature sensor for the off_temp variable, which also influences the min_temp with an additional selector. But the fan speed doesnā€™t seem to change. Do you have any tips/remarks? Cheers.

blueprint:
  name: Auto fan speed sensor
  description: "Temperature based Auto fan control.\n\n Fan Speed will be set when
    initially turned on by relating the ambient temperature to an equivalent speed
    setting. \nA time delay and a minimum percentage change is used to eliminate frequent
    speed changes.\n At the minimum temperature setting the fan will turn off. \n
    When the temperature rises above this minimum temperature setting, the fan will
    automatically turn back on."
  domain: automation
  input:
    temp_sensor:
      name: "\U0001F321 Temperature Sensor"
      description: Enter your temperature sensor. Make sure the temperature sensor
        is in the units (Ā°C or Ā°F) you want to use for the settings below. If your
        temp sensor is in Ā°C but you want to use the Ā°F settings, you will need to
        create a template sensor to convert the sensor into the correct units.
      default: []
      selector:
        entity:
          domain:
          - sensor
          device_class:
          - temperature
          multiple: false
    off_temp_sensor:
      name: "\U0001F321 Off Temperature Sensor"
      description: "Enter a different temperature sensor to determine when the fan should turn off. This sensor will also be used to determine the minimum temperature setting."
      default: []
      selector:
        entity:
          domain:
          - sensor
          device_class:
          - temperature
          multiple: false
    temp_adjustment:
      name: "\U0001F321 Temperature Adjustment"
      description: "Adjust the temperature added to the off temperature for setting the minimum fan speed temperature."
      default: 2
      selector:
        number:
          min: 0.0
          max: 10.0
          step: 0.5
          mode: slider
          unit_of_measurement: 'Ā°C'
    fan_switch:
      name: "\U0001F4A8 Fan"
      description: The fan you wish to speed control.
      selector:
        entity:
          domain:
          - fan
          multiple: false
    min_fan_speed:
      name: "\U0001F4A8 Minimum Fan Speed"
      description: Set the minimum percentage speed when your fan is still on.
      default: 16
      selector:
        number:
          min: 1.0
          max: 100.0
          mode: slider
          step: 1.0
          unit_of_measurement: '%'
    max_fan_speed:
      name: "\U0001F4A8 Maximum Fan Speed"
      description: Set the maximum percentage speed for your fan.
      default: 100
      selector:
        number:
          min: 1.0
          max: 100.0
          mode: slider
          step: 1.0
          unit_of_measurement: '%'
    max_temp:
      name: "\U0001F4C8 Maximum Temperature"
      description: What temperature would you like the fan to run at max fan speed.
      default: 40
      selector:
        number:
          min: 10.0
          max: 120.0
          step: 1.0
          mode: slider
    auto_turn_on_enabled:
      name: āœ… Enable auto fan on
      description: "Let the fan automatically turn back on if the temperature returns to a value above the temperature that the fan switches off."
      default: true
      selector:
        boolean: {}
    change_time:
      name: ā±ļø Change frequency delay
      description: How long to delay between potential speed adjustments.
      default: 30
      selector:
        number:
          min: 1.0
          max: 120.0
          unit_of_measurement: minutes
          step: 1.0
          mode: slider
    change_threshold:
      name: Minimum percentage change
      description: The minimum percentage change (between current fan speed and set
        fan speed)
      default: 1
      selector:
        number:
          min: 1.0
          max: 100.0
          mode: slider
          step: 1.0
          unit_of_measurement: '%'
    max_change:
      name: Maximum percentage change
      description: The maximum percentage change (between current fan speed and set
        fan speed)
      default: 50
      selector:
        number:
          min: 1.0
          max: 100.0
          mode: slider
          step: 1.0
          unit_of_measurement: '%'
    blocker_entity:
      name: (OPTIONAL) Blocking entity
      description: If this entity's state is on, it will prevent the automation from
        running. E.g. sleep mode or away mode.
      default:
      selector:
        entity:
          multiple: false
  source_url: https://community.home-assistant.io/t/adaptive-fan-speed-control-based-on-temperature-and-speed-range/678152
variables:
  temp_sensor: !input temp_sensor
  off_temp_sensor: !input off_temp_sensor
  temp_adjustment: !input temp_adjustment
  fan_switch: !input fan_switch
  auto_turn_on_enabled: !input auto_turn_on_enabled
  min_fan_speed: !input min_fan_speed
  max_fan_speed: !input max_fan_speed
  max_temp: !input max_temp
  current_temp: '{{ states(temp_sensor) | float(0)}}'
  off_temp: '{{ states(off_temp_sensor) | float(0)}}'
  min_temp: '{{ off_temp + temp_adjustment }}'
  change_time: !input change_time
  max_change: !input max_change
  change_threshold: !input change_threshold
  blocker_entity: !input blocker_entity
  fan_speed: '{{ state_attr(fan_switch,''percentage'') | float(0)}}'
  temp_range: '{{ max_temp | float(0) - min_temp | float(0)}}'
  fan_range: '{{ max_fan_speed | float(0) - min_fan_speed | float(0)}}'
  slope: '{{ fan_range | float(0)/temp_range | float(0)}}'
  initial_set_fan_speed: '{{ [[slope|float(0) * (current_temp|float(0) - min_temp|float(0))
    + min_fan_speed|float(0),  min_fan_speed] | max, max_fan_speed] | min}}'
  adjusted_set_fan_speed: "{% set diff = initial_set_fan_speed - fan_speed %} {% if
    diff > max_change|float(0) %}\n  {{ fan_speed + max_change|float(0) }}\n{% elif
    diff < -max_change|float(0) %}\n  {{ fan_speed - max_change|float(0) }}\n{% else
    %}\n  {{ initial_set_fan_speed }}\n{% endif %}\n"
  set_fan_speed: '{{ adjusted_set_fan_speed }}'
  speed_diff: '{{(fan_speed - set_fan_speed)|abs}}'
trigger:
- platform: state
  entity_id:
  - !input fan_switch
  id: fanon
  from: 'off'
  to: 'on'
- platform: state
  id: temp_state_change
  entity_id:
  - !input temp_sensor
condition:
- condition: template
  alias: Check for blocker entity
  value_template: '{{ (blocker_entity == none) or (states(blocker_entity) == ''off'')}}'
action:
- choose:
  - conditions:
    - condition: trigger
      alias: Only run if fan was switched on
      id: fanon
    - condition: template
      alias: Is the percentage change great enough?
      value_template: '{{ speed_diff | float(0) > change_threshold | float(0) }}'
    sequence:
    - service: homeassistant.turn_on
      data:
        percentage: '{{set_fan_speed}}'
      target:
        entity_id: '{{fan_switch}}'
    - delay:
        minutes: !input change_time
  - conditions:
    - condition: template
      value_template: '{{states(fan_switch) == ''on''}}'
      alias: Make sure the fan is already on (ignore if it's been switched off)
    - condition: template
      alias: Is the percentage change great enough?
      value_template: '{{ speed_diff | float(0) > change_threshold | float(0) }}'
    - condition: template
      alias: Is the temperature above the off temp?
      value_template: '{{ current_temp | float(0) > off_temp | float(0) }}'
    sequence:
    - service: homeassistant.turn_on
      target:
        entity_id: '{{fan_switch}}'
      data:
        percentage: '{{set_fan_speed}}'
    - delay:
        minutes: !input change_time
  - conditions:
    - condition: not
      alias: Make sure the fan hasn't just been turned on
      conditions:
      - condition: trigger
        id: fanon
    - condition: template
      alias: Check if the temperature is below off_temp
      value_template: '{{ states(off_temp_sensor) | float(0) < (off_temp | float(0)) }}'
    sequence:  
    - service: homeassistant.turn_off
      target:
        entity_id: '{{fan_switch}}'
  - conditions:
    - '{{ auto_turn_on_enabled }}'
    - condition: template
      value_template: '{{states(fan_switch) == ''off''}}'
      alias: Is the fan currently off?
    - condition: template
      alias: Is the temperature above the off temp?
      value_template: '{{ current_temp | float(0) > off_temp | float(0) }}'
    sequence:
    - service: homeassistant.turn_on
      data:
        percentage: '{{set_fan_speed}}'
      target:
        entity_id: '{{fan_switch}}'
    - delay:
        minutes: !input change_time
mode: single

Is there a change to Integration the climate entity directly?
Currently i need to create a extra Fan Sensor for that

i added humidity control.
it works the same way. it calculates fan speed for temp and humidity and chooses the higher fan speed.

blueprint:
  name: Auto fan speed based on Temperature and Humidity
  description: "Auto fan control based on both temperature and humidity.\n\n The fan speed will be set according to the higher of the two calculated speeds: one based on ambient temperature and the other based on humidity. \nA time delay and a minimum percentage change is used to eliminate frequent speed changes.\n At the minimum settings for either, the fan will turn off."
  domain: automation
  input:
    temp_sensor:
      name: šŸŒ” Temperature Sensor
      description: Enter your temperature sensor.
      default: []
      selector:
        entity:
          domain:
          - sensor
          device_class:
          - temperature
          multiple: false
    humidity_sensor:
      name: šŸ’§ Humidity Sensor
      description: Enter your humidity sensor.
      default: []
      selector:
        entity:
          domain:
          - sensor
          device_class:
          - humidity
          multiple: false
    fan_switch:
      name: šŸ’Ø Fan
      description: The fan you wish to speed control.
      selector:
        entity:
          domain:
          - fan
          multiple: false
    min_fan_speed:
      name: šŸ’Ø Minimum Fan Speed
      description: Set the minimum percentage speed when your fan is still on.
      default: 16
      selector:
        number:
          min: 1.0
          max: 100.0
          mode: slider
          step: 1.0
          unit_of_measurement: '%'
    max_fan_speed:
      name: šŸ’Ø Maximum Fan Speed
      description: Set the maximum percentage speed for your fan.
      default: 100
      selector:
        number:
          min: 1.0
          max: 100.0
          mode: slider
          step: 1.0
          unit_of_measurement: '%'
    max_temp:
      name: šŸ“ˆ Maximum Temperature
      description: The temperature at which the fan will run at max speed.
      default: 40
      selector:
        number:
          min: 10.0
          max: 120.0
          step: 1.0
          mode: slider
    min_temp:
      name: šŸ“‰ Minimum Temperature
      description: The temperature at which the fan will run at minimum speed.
      default: 23
      selector:
        number:
          min: 10.0
          max: 120.0
          step: 1.0
          mode: slider
    max_humidity:
      name: šŸ“ˆ Maximum Humidity
      description: The humidity level at which the fan will run at max speed.
      default: 80
      selector:
        number:
          min: 10.0
          max: 100.0
          step: 1.0
          mode: slider
    min_humidity:
      name: šŸ“‰ Minimum Humidity
      description: The humidity level at which the fan will run at minimum speed.
      default: 40
      selector:
        number:
          min: 10.0
          max: 100.0
          step: 1.0
          mode: slider
    off_temp:
      name: šŸ›‘ The temperature at which the fan switches off
      description: The temperature below which the fan turns off.
      default: 22
      selector:
        number:
          min: 10.0
          max: 120.0
          step: 1.0
          mode: slider
    off_humidity:
      name: šŸ›‘ The humidity level at which the fan switches off
      description: The humidity level below which the fan turns off.
      default: 35
      selector:
        number:
          min: 10.0
          max: 100.0
          step: 1.0
          mode: slider
    auto_turn_on_enabled:
      name: āœ… Enable auto fan on
      description: Let the fan automatically turn back on if the temperature or humidity returns to a value above the off thresholds.
      default: true
      selector:
        boolean: {}
    change_time:
      name: ā±ļø Change frequency delay
      description: How long to delay between potential speed adjustments.
      default: 30
      selector:
        number:
          min: 1.0
          max: 120.0
          unit_of_measurement: minutes
          step: 1.0
          mode: slider
    change_threshold:
      name: Minimum percentage change
      description: The minimum percentage change (between current fan speed and set fan speed).
      default: 1
      selector:
        number:
          min: 1.0
          max: 100.0
          mode: slider
          step: 1.0
          unit_of_measurement: '%'
    max_change:
      name: Maximum percentage change
      description: The maximum percentage change (between current fan speed and set fan speed).
      default: 50
      selector:
        number:
          min: 1.0
          max: 100.0
          mode: slider
          step: 1.0
          unit_of_measurement: '%'
    blocker_entity:
      name: (OPTIONAL) Blocking entity
      description: If this entity's state is on, it will prevent the automation from running. E.g., sleep mode or away mode.
      default: []
      selector:
        entity:
          multiple: false

  source_url: https://community.home-assistant.io/t/adaptive-fan-speed-control-based-on-temperature-and-speed-range/678152
variables:
  temp_sensor: !input temp_sensor
  humidity_sensor: !input humidity_sensor
  fan_switch: !input fan_switch
  auto_turn_on_enabled: !input 'auto_turn_on_enabled'

  min_fan_speed: !input min_fan_speed
  max_fan_speed: !input max_fan_speed

  max_temp: !input max_temp
  min_temp: !input min_temp
  off_temp: !input off_temp

  max_humidity: !input max_humidity
  min_humidity: !input min_humidity
  off_humidity: !input off_humidity

  change_time: !input change_time
  max_change: !input max_change
  change_threshold: !input change_threshold
  blocker_entity: !input blocker_entity
  current_temp: "{{ states(temp_sensor) | float(0)}}"
  current_humidity: "{{ states(humidity_sensor) | float(0)}}"
  fan_speed: '{{ state_attr(fan_switch,''percentage'') | float(0)}}'

  # Temperature-based fan speed calculation
  temp_range: '{{ max_temp | float(0) - min_temp | float(0) }}'
  temp_slope: '{{ (max_fan_speed | float(0) - min_fan_speed | float(0)) / temp_range }}'
  temp_fan_speed: '{{ [[temp_slope * (current_temp - min_temp) + min_fan_speed, min_fan_speed] | max, max_fan_speed] | min }}'

  # Humidity-based fan speed calculation
  humidity_range: '{{ max_humidity | float(0) - min_humidity | float(0) }}'
  humidity_slope: '{{ (max_fan_speed | float(0) - min_fan_speed | float(0)) / humidity_range }}'
  humidity_fan_speed: '{{ [[humidity_slope * (current_humidity - min_humidity) + min_fan_speed, min_fan_speed] | max, max_fan_speed] | min }}'

  # Select the higher fan speed between temperature and humidity
  set_fan_speed: '{{ [temp_fan_speed, humidity_fan_speed] | max }}'

  speed_diff: '{{ (fan_speed - set_fan_speed) | abs }}'

trigger:
- platform: state
  entity_id:
  - !input fan_switch
  id: fanon
  from: 'off'
  to: 'on'
- platform: state
  id: temp_state_change
  entity_id:
  - !input temp_sensor
- platform: state
  id: humidity_state_change
  entity_id:
  - !input humidity_sensor
condition:
- condition: template
  alias: Check for blocker entity
  value_template: '{{ (blocker_entity == none) or (states(blocker_entity) == ''off'')}}'
action:
- choose:
  - conditions:
    - condition: trigger
      alias: Only run if fan was switched on
      id: fanon
    - condition: template
      alias: Is the percentage change great enough?
      value_template: '{{ speed_diff | float(0) > change_threshold | float(0) }}'
    sequence:
    - service: homeassistant.turn_on
      data:
        percentage: '{{ set_fan_speed }}'
      target:
        entity_id: '{{ fan_switch }}'
    - delay:
        minutes: !input change_time
  - conditions:
    - condition: template
      value_template: '{{ states(fan_switch) == ''on'' }}'
      alias: Make sure the fan is already on (ignore if it's been switched off)
    - condition: template
      alias: Is the percentage change great enough?
      value_template: '{{ speed_diff | float(0) > change_threshold | float(0) }}'
    - condition: template
      alias: Is the temperature or humidity above the off thresholds?
      value_template: >
        {{ current_temp > off_temp or current_humidity > off_humidity }}
    sequence:
    - service: homeassistant.turn_on
      target:
        entity_id: '{{ fan_switch }}'
      data:
        percentage: '{{ set_fan_speed }}'
    - delay:
        minutes: !input change_time
  - conditions:
    - condition: not
      alias: Make sure the fan hasn't just been turned on
      conditions:
      - condition: trigger
        id: fanon
    - condition: template
      alias: Should the fan turn off based on both temperature and humidity?
      value_template: '{{ current_temp <= off_temp and current_humidity <= off_humidity }}'
    sequence:
    - service: homeassistant.turn_off
      target:
        entity_id: '{{ fan_switch }}'
  - conditions:
    - '{{ auto_turn_on_enabled }}'
    - condition: template
      value_template: '{{ states(fan_switch) == ''off'' }}'
      alias: Is the fan currently off?
    - condition: template
      alias: Is either the temperature or humidity above the off thresholds?
      value_template: '{{ current_temp > off_temp or current_humidity > off_humidity }}'
    sequence:
    - service: homeassistant.turn_on
      data:
        percentage: '{{ set_fan_speed }}'
      target:
        entity_id: '{{ fan_switch }}'

oh wow thatā€™s a great idea and a simple/tidy implementation! Nice work! My only suggestion would have been make the temp and humidity variables optional then during the set_fan_speed calculation, check if one or the other (or both) is undefined/null and perform the calculation accordingly. That way, you get to decide if you only want temperature control, or only humidity control, or both!

The check could be done like:

# Select the higher fan speed between temperature and humidity, ignoring None values
set_fan_speed: '{{ [temp_fan_speed | default(0), humidity_fan_speed | default(0)] | max }}'

Explanation:

  • | default(0) ensures that if temp_fan_speed or humidity_fan_speed is None, it is replaced with 0 (or any other fallback value you want).
  • The max function then compares the two values after replacing None with 0.
1 Like

Yeah iā€™m not sure of the best way to do this other than creating a seperate fan entity as you have done. I donā€™t have enought experience with how to handle the service calls for a climate enitity and neatly that could be done while still keeping the fan entity functionality