Fan Template for two speed house fan

I have created a control for a two speed, whole house fan based on the fan template and python scripts. Further down, you will see I need a little help with the “speed_template” for the fan, I can’t get this to compile

The fan is a two-speed fan which has neutral and then two active wires. Energize red and its low speed, energize black for high speed. But do not energize both at the same time! The fan is pretty big, the motor plate reports 6.5A on hi and 3.7A on Lo.

I wanted a hardware solution to prevent energizing both speed wires at the same time. I did not want to rely on a software protocol to overcome an unsafe hardware arrangement (cite Boeing Max 8).

So I have Aeotec Heavy Duty Switch as the master on/off. I chose this to handle the large inductive load. Smaller “lighting” switches appear to have the current rating but that is not for an inductive load (such as a motor).

Then I have an a Aeotec Nano switch + Digital Loggers AC Power Control Relay. The Nano switch controls the relay and the relay routes the active (from the Heavy Duty Switch) from its COMMON terminal to either NO or NC terminal. The NO / NC termainal then connect to red / black on the motor. Becasue the relay is SPDT its physically impossible to power the motor’s red and black wires at the same time.
=> Note I had some trouble with the Digital Logger’s relay. Its overload protection trips occasionally when the motor starts. So I disabled this protection, which I feel is safe given the Heavy Duty Switch before it (and the circuit breaker before that).

So here is my fan template from configuration.yaml

fan:
  - platform: template
    fans:
      house_fan:
        friendly_name : "House Fan"
        value_template : "{{ states('switch.aeotec_zw078_heavy_duty_smart_switch_gen5_switch') }}"
#        speed_template: "{{% if is_state('switch.aeotec_zw139_nano_switch_switch', "on") %}Hi{% else %}Lo{% endif %}}"
#        speed_template: "{% if is_state('switch.aeotec_zw139_nano_switch_switch', "on") %}Hi{% else %}Lo{% endif %}"
        turn_on:
          service: python_script.house_fan_on
        turn_off:
          service: python_script.house_fan_off
        set_speed:
          service: python_script.house_fan_speed
          data_template:
            speed: "{{ speed }}"
        speeds:
          - 'Hi'
          - 'Lo'

I cannot get either of the commented speed_template lines to work.
They always give me configuration errors, even though it works fine if I write them into the web engine’s template editor. Any help appreciated.

Then here are my python scripts.
There are two important things for the scripts to do.

  1. Before turning on the fan we must have adequate air flow. So I open two skylights (or at least ensure they are already open)
  2. The fan has louvers. They open with the airflow and they do not fully open if you start the fan on Lo. So I start the fan on high and then switch to low after a few seconds. The factory controller for the fan does this too.

python_scripts/house_fan_on.py

#
# Script to safely turn on the house fan
#

# Ensure there is adequate airlow
def adequate_airflow(hass, logger):
    # Ensure the skylights are open
    SkyWindow1 = hass.states.get('cover.skylight_1').attributes.copy()
    SkyWindow1 = SkyWindow1.get('current_position')
    SkyWindow2 = hass.states.get('cover.skylight_2').attributes.copy()
    SkyWindow2 = SkyWindow2.get('current_position')
    if (SkyWindow1 < 100 or SkyWindow2 < 100):
        logger.info("Opening Skylight Windows to turn on House Fan")
        hass.services.call('cover', 'open_cover', service_data={ 'entity_id': 'cover.skylight_1' })
        hass.services.call('cover', 'open_cover', service_data={ 'entity_id': 'cover.skylight_2' })
        # Skylights take around a minute to open / close
        # This is a timeout set to 2-3x expected time
        timeout = 180
        while (timeout > 0) :
            timeout = timeout - 1
            SkyWindow1 = hass.states.get('cover.skylight_1').attributes.copy()
            SkyWindow1 = SkyWindow1.get('current_position')
            SkyWindow2 = hass.states.get('cover.skylight_2').attributes.copy()
            SkyWindow2 = SkyWindow2.get('current_position')
            if (SkyWindow1 < 100 or SkyWindow2 < 100) :
                time.sleep(1)
            else :
                break
    # Be really sure they are open before returning True
    if (SkyWindow1 == 100 and SkyWindow2 == 100):
        return True
    else :
        return False
    
# main
HouseFan = hass.states.get('switch.aeotec_zw078_heavy_duty_smart_switch_gen5_switch').state
Hi_Lo = hass.states.get('switch.aeotec_zw139_nano_switch_switch').state
if (HouseFan == "off") :
    if (adequate_airflow(hass,logger)) :
        # OK to turn on the house fan
        # Ensure the fan always starts on Hi, so there is enough wind to full open louvers
        hass.services.call('switch', 'turn_off', service_data={ 'entity_id': 'switch.aeotec_zw139_nano_switch_switch' })
        time.sleep(1)
        hass.services.call('switch', 'turn_on', service_data={ 'entity_id': 'switch.aeotec_zw078_heavy_duty_smart_switch_gen5_switch' })
        # Fan Template will call set_seed automatically after this, just insert a little delay here
        # So the fan can get up to full speed and open the louvers
        time.sleep(3)
    else :
        logger.error("Failed to open Skylight Windows for whole house fan, aborting the command to turn on the fan")

I don’t quite understand why I need to pass hass and logger to the function adequare_airflow. I thought I could access global names within a function. Even defining hass and logger to be global within the function did not help

python_scripts/house_fan_off.py

#
# Turn off the house fan
#

# Turn off the fan
hass.services.call('switch', 'turn_off', service_data={ 'entity_id': 'switch.aeotec_zw078_heavy_duty_smart_switch_gen5_switch' })

# Set Hi/Lo to Hi speed
hass.services.call('switch', 'turn_off', service_data={ 'entity_id': 'switch.aeotec_zw139_nano_switch_switch' })

python_scripts/house_fan_speed.py

#
# Set the speed of the fan
#
speed = data.get('speed')

HouseFan = hass.states.get('switch.aeotec_zw078_heavy_duty_smart_switch_gen5_switch').state

# When switch is off==Hi speed, on==Lo speed
Hi_Lo = hass.states.get('switch.aeotec_zw139_nano_switch_switch').state

if (speed == "Hi"):
    hass.services.call('switch', 'turn_off', service_data={ 'entity_id': 'switch.aeotec_zw139_nano_switch_switch' })
    logger.info("Set house fan speed set to Hi")
 elif (speed == "Lo"):
    hass.services.call('switch', 'turn_on', service_data={ 'entity_id': 'switch.aeotec_zw139_nano_switch_switch' })
    logger.info("Set house fan speed set to Lo")

So that’s it so far.
The Nano switch has two inputs S1 / S2. I have wired these to the old factory switch of the fan. This is actually a (proprietary) wireless switch. My thought was to detect S1/S2 and be able to write automations to pass on the commands. But its not obvious how to get the state of S1/S2 from the default state for the nano switch. I probably need to do some reading on the switch and the zwave integration of it.

Oh - The reason for all this is to fully automate the fan, skylights, and shades to minimize the use of the HVAC. This is a work in progress and I’ll post that when its a bit more mature.

This should do the trick for the template:

        speed_template: "{{ 'Hi' if is_state('switch.aeotec_zw139_nano_switch_switch', 'on') else 'Lo' }}"

As for ‘passing hass’ (ahem) I’ve seen the need for doing that in other python examples. The object’s scope is limited to the main body of the code.

Perfect, that works.
Easy as 123 when you know how. :slight_smile:

I know you already found the solution for this but I was looking at the code that you said wasn’t able to work.

In theory the way you had it would have worked in the second speed_template except you had several syntax errors that were giving you the config check errors.

you had an extra space between the “friendly_name” and the “:” after it. You also had an extra space between the “value_template” and the “:” after it. Then you also used the wrong version of quotation marks in the second “speed_template”. you had used the double quotation marks for the “on”. You should have used the single quotation marks as in ‘on’. The quotation mark style inside the template (inside the {{}}) always needs to be the opposite of the ones outside the template.

the reason the first version of the speed_template didn’t work (inaddition to the quotation mark problem) is that you used the wrong syntax for the template enclosure brackets. {% … %} are used for any template expression that needs to perform any processing (such if,else,endif) and you don’t want to print the results. you use {{ … }} that needs some minimal processing and then you want to print the result of the expression inside the brackets.

Once you fixed all of that it should have worked the way you had it:

fan:
  - platform: template
    fans:
      house_fan:
        friendly_name: "House Fan"
        value_template: "{{ states('switch.aeotec_zw078_heavy_duty_smart_switch_gen5_switch') }}"
        speed_template: "{% if is_state('switch.aeotec_zw139_nano_switch_switch', 'on') %}Hi{% else %}Lo{% endif %}"
        turn_on:
          service: python_script.house_fan_on
        turn_off:
          service: python_script.house_fan_off
        set_speed:
          service: python_script.house_fan_speed
          data_template:
            speed: "{{ speed }}"
        speeds:
          - 'Hi'
          - 'Lo'

Hey thanks for the analysis, I will try this and see if I can get my original expressions to work (just for the learning).
The frustrating thing was I was able to cut and paste it into the template screen and it worked there. I am not sure how we explain that!