Best way to create dynamic fan speed adjustments in response to environmental conditions?

I recently purchased an Inkbird “smart” inline duct fan IVC-001W for controlling the environment of an indoor garden. It’s purpose is to ventilate the space in response to changes in temperature and humidity. It has an automatic mode built-in which allows you to set temperature and humidity thresholds for when the fan should run and the speed at which it should run. Unfortunately, the implementation of this mode is not well thought out given the purposes for which it has been marketed. An indoor garden must be kept under negative pressure at all times i.e. the fan should always run at a minimum speed to ensure a constant supply of fresh air is drawn into the space. However, when the temperature and humidity are within tolerance the fan simply reverts to an OFF state. Additionally, the ON fan speed value on the controller can only be set to a static value rather than one that dynamically adjusts in response to the amount of deviation from the environmental setpoints. This is inefficient and has the potential to cause undesirable over/under correction depending on the static value chosen.

Enter home assistant, which I have 0 prior experience with but I figured it could save the day here and smarten up my fan a bit. I’ve been wanting to play with it for a while so this seemed like the perfect excuse. Luckily, the Inkbird fan is a Tuya device, so I have managed to integrate it with Local Tuya and I have control over the fan speed with a numeric value between 0 and 10 in home assistant. I also have temperature and humidity values from a separate sensor in the space. I have now set up some simple automations to set the fan speed to a higher value if environmental conditions exceed thresholds but to always revert to a minimum speed when they are within tolerance, now I just have to set up the dynamic control portion but I’m not sure where to start. Or rather, which of home assistant’s features are best suited to most effectively achieve my goal.

What I want to achieve:

Define adjustable minimum/maximum fan speeds, as well as humidity and temperature limits. Once the limits are reached, for every X degrees C or % points of humidity these values exceed the set value, increase the fan speed by Y fan levels up to the defined maximum.

Example:

Minimum fan speed: 1
Temperature Threshold: 21C
Maximum fan speed: 6
X: 0.5
Y:1

When the temperature reaches 21.5C the fan speed is increased from 1 to 2, at 22C the fan speed increases to 3, at 22.5C it will increase to 4 and so on, up until 23.5C where it will reach the maximum fan speed of 6 and not be increased beyond that. When the temperature drops it should ramp down in the same way until it reaches the minimum fan speed of 1 and remain there when the temperature is 21C or below.

The controller should also handle humidity in the same way. It would also be nice if all these values could be adjusted from sliders on the dashboard. I do currently have helpers set up to adjust the defined temp and humidity setpoints within the automations and I can adjust them on the dash.

What I’m really trying to get to the bottom of is, what should I be using to facilitate the intelligent and dynamic control of the fan speed? Is there a way to achieve this within the automations themselves with additional conditions, triggers and actions or will I need the help of some sort of script or function? I’m sure this is possible given home assistant’s depth, but some guidance on the most effective way to facilitate it would be greatly appreciated. I feel I’m stood dangerously close to the edge of a rather deep rabbit hole here and trying to avoid too much spelunking if I can. Thanks in advance for all your tips and tricks! :smiley:

You could build this out in an automation without any kind of Jinja scripting but it’ll be a mess to manage. You could also use minimal Jinja within the automation to help make it smarter. If it were me I would probably consider either a script or a template fan that adjusts itself automatically as the parameters change.

I have a similar system in my indoor grow tent (where I start my summer veg), and the other thing to consider is oxygen, the fan is also intended to keep a steady supply of CO2 inside the environment and help take O2 out.

1 Like

Hi CO_4x4, thanks for your response.

Yes! If there’s one thing plants crave besides electrolytes, it’s CO2! :rofl: This is why it baffles me that Inkbird designed the controller the way they did without a minimum fan speed in auto mode. (Actually, it does appear to have a DP parameter in the firmware but no way to adjust it in Inkbird or Tuya apps! :man_facepalming: ) I have another tent with an AC Infinity Cloudline T6 which has the behaviour I’m trying to replicate. Beginning to wish I’d just bought another one of those, but at least it’s giving me a good excuse to finally get my feet wet with HA.

Bear in mind this is day 2 of trying to learn HA, so I’m still a greenbeard and I’m far from a programmer, so GUI-based solutions are preferred assuming they’re powerful enough. I can see how using automations alone to do what I want would quickly become a fustercluck.

I’m guessing Jinja is a language, do I need any prerequisite add-ons etc to use this in HA?

How can I use Jinja within automations?

What’s a template fan? I’m guessing I have to define the fan speed control as such in Local Tuya’s configuration as opposed to just a numerical value for the fan level?

Thanks for the pointers, much appreciated, I’ll do some digging into the topics you’ve mentioned, if there are any good resources you can recommend to further my understanding of them, a point in the right direction would be greatly appreciated. :+1:

Nope, that’s just what scripting is called, it’s a Python based code that is very simple to figure out and it is all built-in.

See here: Home Assistant Template Fan

The idea is that you create a new fan device that utilizes other devices to emulate a fan. So your template fan might turn on device A for the fan, device B for the speed, etc. What it gives you in this instance is a place to do things in Jinja like turn on the fan for X minutes and turn it off when you use the UI or automation to turn on your fan.

It’s all pretty easy once you get to know it, but it’s a little daunting as a new user. Here’s an example from my own HA for a template fan. While the Nest already allows me to do this, I needed a separate fan device in HA for other purposes:

upstairs_thermostat:
  friendly_name: "Nest Thermostat fan"
  unique_id: 350d61f0-2045-41ec-81e1-4df310753621
  value_template: "{{ state_attr('climate.upstairs_thermostat', 'fan_mode') }}"
  turn_on:
    service: climate.set_fan_mode
    data:
      fan_mode: "on"
    target:
      entity_id: climate.upstairs_thermostat

  turn_off:
    service: climate.set_fan_mode
    data:
      fan_mode: "off"
    target:
      entity_id: climate.upstairs_thermostat

What you see in the “state” is an example of Jinja, python code that lets me make intelligent decisions about states or operations. I don’t have a complex one for a fan, but I do for many other things. Take this for example:

sensor:
  - name: "Downstairs CO2 Health"
    unique_id: c4d4635d-7e82-41fd-acc8-acacf91fa397
    state: >-
      {% set co2 = float(states('sensor.airthings_wave_192113_co2'), 0) %}
      {% set level = "Normal" %}

      {% if co2 >= 800 and co2 < 1000 %}
        {% set level = "Moderate" %}
      {% elif co2 >= 1000 %}
        {% set level = "High" %}
      {% endif %}

      {{ level }}

    icon: mdi:brain

This is a template sensor that reads my air quality meter to convert the numeric “air quality” to a human readable value, which I use in my voice notifications to tell me when the air quality is poor. This is an example of how Jinja in a template device really takes things not only to a new level, but really customizes devices to your needs.

Here’s how I did it, in case this is useful for anyone else in the future:

This automation controls the fan speed relative to the temperature, you’ll need to set up a bunch of helpers, their names are in the code. I’ve set my fan up to be controlled by a numerical input value between 0-10


alias: Fan Temp Automation
description: ""
trigger:
  - platform: state
    entity_id: sensor.tasmota_am2301_temperature #Replace with your temerature sensor's entity ID 
  - platform: state
    entity_id: input_number.fan_min_speed #define all these helpers
  - platform: state
    entity_id: input_number.fan_max_speed
  - platform: state
    entity_id: input_number.fan_boost_increment #Each time the temperature increases beyond the set point by this value, The fan speed increases by the value of "fan_boost_amount"
  - platform: state
    entity_id: input_number.fan_boost_amount
  - platform: state
    entity_id: input_number.temperature_setpoint
  - platform: state
    entity_id:
      - input_number.humidity_setpoint
  - platform: state
    entity_id:
      - sensor.tasmota_am2301_humidity
  - platform: time_pattern
    seconds: /5
action:
  - service: number.set_value
    target:
      entity_id: number.fan_speed
    data:
      value: >
        {% set min_speed = states('input_number.fan_min_speed') | int %} 
        {% set max_speed = states('input_number.fan_max_speed') | int %}  
        {% set fan_boost_increment = states('input_number.fan_boost_increment') | float %} 
        {% set boost_amount = states('input_number.fan_boost_amount') | int %}
        {% set temp_threshold = states('input_number.temperature_setpoint') | int %} 
        {% set current_temp = states('sensor.tasmota_am2301_temperature') | int %}

        {% if current_temp <= temp_threshold %}
           {{ min_speed | int }}
        {% elif current_temp >= temp_threshold + (max_speed - min_speed) %}
           {{ max_speed | int }}
        {% else %}
           {% set speed_increase = ((current_temp - temp_threshold) / fan_boost_increment) | int %}
           {% set new_speed = min_speed + (speed_increase * boost_amount) | int %}
          {% if new_speed > max_speed %}
             {{ max_speed | int }}
          {% else %}
             {{ new_speed | int }}
           {% endif %}
        {% endif %}

This is a separate automation that controls the fan based on the humidity, It behaves the same way but instead uses the input from the humidity sensor and the value of the humidity set point helper.

alias: Fan Humidity Automation
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.tasmota_am2301_humidity
  - platform: state
    entity_id: input_number.fan_min_speed
  - platform: state
    entity_id: input_number.fan_max_speed
  - platform: state
    entity_id: input_number.fan_boost_increment
  - platform: state
    entity_id: input_number.fan_boost_amount
  - platform: state
    entity_id:
      - input_number.humidity_setpoint
  - platform: state
    entity_id:
      - sensor.tasmota_am2301_temperature
  - platform: state
    entity_id:
      - input_number.temperature_setpoint
  - platform: time_pattern
    seconds: /5
condition: []
action:
  - service: number.set_value
    target:
      entity_id: number.fan_speed
    data:
      value: >
        {% set min_speed = states('input_number.fan_min_speed') | int %} 
        {% set max_speed = states('input_number.fan_max_speed') | int %}  
        {% set fan_boost_increment = states('input_number.fan_boost_increment') | float %} 
        {% set boost_amount = states('input_number.fan_boost_amount') | int %}
        {% set humidity_threshold = states('input_number.humidity_setpoint') | int %} 
        {% set current_humidity = states('sensor.tasmota_am2301_humidity') | int %}

        {% if current_humidity <= humidity_threshold %}
           {{ min_speed | int }}
        {% elif current_humidity >= humidity_threshold + (max_speed - min_speed) %}
           {{ max_speed | int }}
        {% else %}
           {% set speed_increase = ((current_humidity - humidity_threshold) / fan_boost_increment) | int %}
           {% set new_speed = min_speed + (speed_increase * boost_amount) | int %}
          {% if new_speed > max_speed %}
             {{ max_speed | int }}
          {% else %}
             {{ new_speed | int }}
           {% endif %}
        {% endif %}

I wrote a third automation to prioritise control of the fan speed by the other 2 automations, based on which environmental condition is furthest away from it’s set point.

alias: Automation Priority Allocator
description: ""
trigger:
  - platform: state
    entity_id: sensor.tasmota_am2301_temperature
  - platform: state
    entity_id: sensor.tasmota_am2301_humidity
  - platform: state
    entity_id: input_number.fan_min_speed
  - platform: state
    entity_id: input_number.fan_max_speed
  - platform: state
    entity_id: input_number.fan_boost_increment
  - platform: state
    entity_id: input_number.fan_boost_amount
  - platform: state
    entity_id:
      - input_number.humidity_setpoint
  - platform: state
    entity_id:
      - input_number.temperature_setpoint
action:
  - service: automation.turn_on
    data_template:
      entity_id: >
        {% set temperature_deviation =
        states('sensor.tasmota_am2301_temperature') | float -
        states('input_number.temperature_setpoint') | float %}

        {% set humidity_deviation = states('sensor.tasmota_am2301_humidity') |
        float - states('input_number.humidity_setpoint') | float %}      

        {% if temperature_deviation > humidity_deviation %}
          automation.temp_fan_automation
        {% else %}
          automation.humidity_fan_automation
        {% endif %}
  - service: automation.turn_off
    data_template:
      entity_id: >
        {% set temperature_deviation =
        states('sensor.tasmota_am2301_temperature') | float -
        states('input_number.temperature_setpoint') | float %}

        {% set humidity_deviation = states('sensor.tasmota_am2301_humidity') |
        float - states('input_number.humidity_setpoint') | float %}

        {% if temperature_deviation > humidity_deviation %}
          automation.humidity_fan_automation
        {% else %}
          automation.temp_fan_automation
        {% endif %}
1 Like

Hi Lionel,

This looks like just sort of what i need to control my ventilation system to increase/decrease speed depending on CO2 meassurements from a airthing.

I have a couple of questions though, hope you have the time to help me out :slight_smile:

You write that it is needed to create a lot of helpers, but how do i set those helpers up?

My ventilation system needs to run in normal mode ( 60% exhaust fan and 55% inlet fan) and i want it to increase those two fans 5% up for every 100PPM my CO2 increases above 1000PPM. And of cause i want it to regulate down again in the same pathern.

Is this something that is possible ? :slight_smile:

/Kasper