Fully automated luxury home climate with Home Assistant

published Aug 16, 2023, last modified May 22, 2024

If you live in a Home Assistant smart home, there's no need to remember to turn off the A/C or the heating, like a caveman.

Fully automated luxury home climate with Home Assistant

If you're anything like me, you might have a lot on your mind... which leads you to forget doing trivial things.  Like turning off the air conditioner when you leave home, or you open a window.

Do not despair.  Here is a sample Home Assistant automation that does it all for you, complete with code.

Thanks to this setup (and the prior high-precision thermostat work) we no longer need to do anything with the A/C.  When it needs to run, it runs.  When it doesn't need to, it's off.  And when it's running, the temperature is just perfect for us.

Of course, you can modify the automation as you see fit — perhaps you'd like your heaters to turn off when you leave... and you can do that too.

This is how my dashboard looks like when the automation has kicked in.  I will soon share how this dashboard is set up:

Requisites

This automation depends on the following:

  • A climate entity.  In the sample below, it's called climate.living_space.
    • Note that you can modify the automation to govern your home heating climate entities as well.
  • An input boolean that lets you suspend the A/C unit.  In the sample below, it's called input_boolean.air_conditioning_suspended.
  • Some sensors for certain states at home, which will be inputs for the automation.  In the sample below, they are:
    • binary_sensor.indoors_windows
      • You will need to create this sensor by adding a group helper that includes all your indoor windows through which the air conditioner may escape.
      • Flips to on after we open a window or a door that lets outside air in.
      • Flips to off immediately after all windows and doors communicating with the outside are closed
      • Use whatever makes sense for your home.
    • binary_sensor.natural_ventilation_active
      • It's pretty much the same as the previous sensor, but with the following caveat: in this sensor, entranceway doors only cause the sensor to flip to on after thirty seconds.
      • Copy/paste code below lets you implement this sensor, but you need to modify the code to include all your entranceway doors.
      • The purpose of this sensor is to allow the air conditioner to run for a little longer than 5 seconds — well, half a minute — after a door has been opened.  This gives us a bit of time between the A/C detecting us at home, and us walking in with the entranceway door open.
      • The additional purpose for this complexity is that this sensor takes into account things like, Is the bedroom door closed?  then it doesn't matter if the windows of the bedroom are open.  You can implement your air flow logic in this sensor.
    • binary_sensor.no_ventilation_for_air_conditioner_compressor
      • Copy/paste code below lets you implement this sensor, but you need to modify it to include any windows ventilating the area of your home that has the compressor.  If you have no such windows, you can neuter the sensor by making its value equal to true always.
      • Flips to on if the windows where the compressor part of the air conditioner are closed.  It's a safety mechanism to prevent that room from getting to 50 degrees Celsius.
    • binary_sensor.someone_home
      • Flips to off when no one is home, and on back when someone is detected at home.
      • You must create this sensor by using copy/paste code below, but you have to modify it to include all entities you want to track that indicate someone is present.  In the example, we use the zone.home counter sensor, which is quite the sane default.
      • My sample here also uses a Home occupancy mode input select, with value Guest at home for when there are guests and we are not around.
    • input_boolean.vacation_mode
      • This one is the simplest — we enable it when we go on vacation, and we turn it off (or, rather, an automation turns it off) when we return home.
      • You will need to create this input boolean.

What does the automation do?

The tasks the automation makes itself responsible for are very straightforward:

  1. If any one of these sensor conditions flips to true1, then (depending on the condition, maybe after a few seconds, or maybe immediately) the input_boolean.air_conditioning_suspended is flipped to true.
  2. If all of these sensor conditions flip to false1, then the input_boolean.air_conditioning_suspended is flipped to false.
  3. Whenever the input_boolean.air_conditioning_suspended boolean is flipped to true, the air conditioner is commanded to turn off.
  4. Whenever the input_boolean.air_conditioning_suspended boolean is flipped to false, the air conditioner is commanded to turn on.
  5. If the air conditioner is turned on manually, the boolean is flipped to false — because, obviously, the user's intention was to run the unit anyway.

Code

Configuration file sensor additions

Here is the configuration you must add to your configuration.yaml to implement the binary sensors mentioned above.  You should of course replace the entities mentioned in the sensors with entities that make sense to you:

template:
  - name: Someone home
    unique_id: someone_home
    availability: "{{ is_state(\"input_select.home_occupancy_mode\", \"guest at home\") }}"
    delay_off: 0:00:05
    device_class: occupancy
    icon: "{%\n  if\n  is_state(\"input_select.home_occupancy_mode\", \"guest at home\")\n%}mdi:home-floor-g{%\n
      \ else\n%}mdi:home-floor-{{ states(\"zone.home\") | int }}{%\n  endif\n%}"
    state: "{%\n  if\n  is_state(\"input_select.home_occupancy_mode\", \"guest at
      home\")\n%}True{%\n  else\n%}{{ (states(\"zone.home\") | int)
      > 0}}{%\n  endif\n%}"
  # The following sensor you can dispense with by making it true always
  # if your air conditioner compressor is not in a room with windows.
  - binary_sensor:
    name: No ventilation for air conditioner compressor
    unique_id: no_ventilation_for_air_conditioner_compressor
    state: >-
        {{-
        is_state("binary_sensor.entranceway_northwest_window_opening", "off")
        or is_state("binary_sensor.entranceway_middle_window_opening", "off")
        -}}
    attributes:
        message: Open the two entranceway windows closest to the compressor.
        refers_to: climate
    availability: >-
        {{-
        has_value("binary_sensor.entranceway_northwest_window_opening")
        and has_value("binary_sensor.entranceway_middle_window_opening")
        -}}
    device_class: opening
  - binary_sensor:
    name: Natural ventilation active
    unique_id: natural_ventilation_active
    state: >-
        {%- if
        (is_state("binary_sensor.entranceway_sliding_door_opening", "off")
        or is_state("binary_sensor.office_door_opening", "off")
        or has_value("climate.novamatic_cl_1590"))
        and
        (is_state("binary_sensor.entranceway_door_opening", "off")
        or is_state("binary_sensor.entranceway_windows", "off"))
        and
        (is_state("binary_sensor.gym_door_opening", "off")
        or is_state("binary_sensor.gym_windows", "off"))
        and
        (is_state("binary_sensor.master_bedroom_door_opening", "off")
        or is_state("binary_sensor.master_bedroom_windows", "off"))
        and
        is_state("binary_sensor.loggia_sliding_door_opening", "off")
        and
        is_state("binary_sensor.dining_room_window_opening", "off")
        and
        is_state("binary_sensor.living_room_windows", "off")
        %}off{%
        else
        %}on{%
        endif
        -%}
    attributes:
        message: Close all windows and doors that let air into the home to resume air conditioning.
        refers_to: climate
    availability: >-
        {{ not expand([
            "binary_sensor.entranceway_sliding_door_opening",
            "binary_sensor.office_door_opening",
            "binary_sensor.entranceway_door_opening",
            "binary_sensor.entranceway_windows",
            "binary_sensor.gym_door_opening",
            "binary_sensor.gym_windows",
            "binary_sensor.master_bedroom_door_opening",
            "binary_sensor.master_bedroom_windows",
            "binary_sensor.loggia_sliding_door_opening",
            "binary_sensor.dining_room_window_opening",
            "binary_sensor.living_room_windows",
        ]) | selectattr('state', 'in', ['unavailable', 'unknown']) | list
        }}
    device_class: opening

Automation

alias: "A/C: suspend or restore as needed"
description: ""
trigger:
  - platform: state
    entity_id:
      - binary_sensor.natural_ventilation_active
    to: "on"
    for:
      hours: 0
      minutes: 0
      seconds: 30
    id: outdoor_access_open
  - platform: state
    entity_id:
      - binary_sensor.indoors_windows
    to: "on"
    for:
      hours: 0
      minutes: 0
      seconds: 5
    id: windows_open
  - platform: state
    entity_id:
      - binary_sensor.no_ventilation_for_air_conditioner_compressor
    from: "on"
    to: "off"
    id: ventilation_on
  - platform: template
    value_template: >-
      {{ not (is_state("input_boolean.vacation_mode", "off") and
      is_state("binary_sensor.someone_home", "on")) }}
    id: away_or_vacay
    alias: No one home or vacay
  - platform: state
    entity_id:
      - binary_sensor.natural_ventilation_active
    for:
      hours: 0
      minutes: 0
      seconds: 5
    to: "off"
    id: outdoor_access_closed
  - platform: state
    entity_id:
      - binary_sensor.indoors_windows
    to: "off"
    for:
      hours: 0
      minutes: 0
      seconds: 5
    id: windows_closed
  - platform: state
    entity_id:
      - binary_sensor.no_ventilation_for_air_conditioner_compressor
    from: "off"
    to: "on"
    id: ventilation_off
  - platform: template
    value_template: >-
      {{ is_state("input_boolean.vacation_mode", "off") and
      is_state("binary_sensor.someone_home", "on") }}
    id: someone_home
    alias: Someone home without vacay
  - platform: state
    entity_id:
      - input_boolean.air_conditioning_suspended
    from: "off"
    to: "on"
    alias: suspended
    id: suspended
  - platform: state
    entity_id:
      - input_boolean.air_conditioning_suspended
    from: "on"
    to: "off"
    alias: unsuspended
    id: unsuspended
  - platform: state
    entity_id:
      - climate.living_space
    from: "off"
    to: cool
    id: turned_on
condition:
  - condition: template
    value_template: "{{ has_value(\"climate.living_space\") }}"
    alias: A/C controller is available
action:
  - choose:
      - conditions:
          - condition: or
            conditions:
              - condition: trigger
                id:
                  - outdoor_access_open
                  - away_or_vacay
              - condition: and
                conditions:
                  - condition: trigger
                    id:
                      - windows_open
                  - condition: state
                    entity_id: binary_sensor.natural_ventilation_active
                    state: "on"
                alias: Windows open with outside air access
              - condition: trigger
                id:
                  - ventilation_off
                alias: Compressor ventilation off
          - condition: template
            value_template: >-
              {{ is_state("climate.living_space", "cool") or
              is_state("climate.living_space", "dry") or
              is_state("climate.living_space", "fan_only") }}
            alias: A/C on
          - condition: state
            entity_id: input_boolean.air_conditioning_suspended
            state: "off"
        sequence:
          - service: input_boolean.turn_on
            data: {}
            target:
              entity_id: input_boolean.air_conditioning_suspended
      - conditions:
          - condition: and
            conditions:
              - condition: trigger
                id:
                  - outdoor_access_closed
                  - windows_closed
                  - ventilation_on
                  - someone_home
            alias: >-
              Windows or outside access closed or return to home or ventilation
              for A/C restored
          - condition: state
            entity_id: binary_sensor.no_ventilation_for_air_conditioner_compressor
            state: "off"
          - condition: state
            entity_id: binary_sensor.natural_ventilation_active
            state: "off"
          - condition: state
            entity_id: binary_sensor.indoors_windows
            state: "off"
          - condition: state
            entity_id: binary_sensor.someone_home
            state: "on"
          - condition: state
            entity_id: input_boolean.air_conditioning_suspended
            state: "on"
        sequence:
          - service: input_boolean.turn_off
            data: {}
            target:
              entity_id: input_boolean.air_conditioning_suspended
            enabled: true
      - conditions:
          - condition: trigger
            id:
              - turned_on
          - condition: state
            entity_id: input_boolean.air_conditioning_suspended
            state: "on"
        sequence:
          - service: input_boolean.turn_off
            data: {}
            target:
              entity_id: input_boolean.air_conditioning_suspended
      - conditions:
          - condition: trigger
            id:
              - suspended
          - condition: state
            entity_id: climate.living_space
            state: cool
        sequence:
          - service: climate.turn_off
            data: {}
            target:
              entity_id:
                - climate.living_space
          - wait_for_trigger:
              - platform: state
                entity_id:
                  - climate.living_space
                to: "off"
                for:
                  hours: 0
                  minutes: 0
                  seconds: 0
            timeout:
              hours: 0
              minutes: 0
              seconds: 10
              milliseconds: 0
            alias: Wait for living space to change to off
      - conditions:
          - condition: trigger
            id:
              - unsuspended
          - condition: state
            entity_id: climate.living_space
            state: "off"
        sequence:
          - service: climate.turn_on
            data: {}
            target:
              entity_id:
                - climate.living_space
            enabled: true
          - wait_for_trigger:
              - platform: state
                entity_id:
                  - climate.living_space
                to: cool
                for:
                  hours: 0
                  minutes: 0
                  seconds: 0
            timeout:
              hours: 0
              minutes: 0
              seconds: 10
              milliseconds: 0
            alias: Wait for living space to change to cool
mode: queued
max: 10