I have a handful of helpers in my Home Assistant setup that track the basic state of a few things. For example, we have a simple "guest mode" boolean that will turn on or off certain automatons when we have guests, and for my bin reminder automation, I use a few helpers to quickly determine when an automation last run, or if a notification needs to be sent.

A few months ago, our first child was born, and we quickly discovered how certain home automations needed to be tweaked to better deal with the middle-of-the-night wake-ups that are an inevitable necessity with a newborn. While having lights automatically turn on based on motion in the evening, and the run-up to going to bed is useful, when these same automations kick in at 2 am or 3 am when you're half asleep and need to change a nappy, it is somewhat unwelcome in comparison.

As a result, a small handful of automations needed to be adapted to behave slightly differently at night. On the surface, this isn't too complicated at all. We could get a basic implementation working by using a boolean helper that we manually toggle to determine if we're in "night mode" or not and behave accordingly.

I wanted to avoid having a button or a helper that we'd have to manually trigger for Home Assistant to understand if we're in night mode or not. I wanted an implementation that was more sophisticated than this because, while we typically go to bed at a reasonably consistent time, the reality is that this does fluctuate. Sometimes, one of us might go to bed before the other, or we may have family staying, so we are more likely to all go to bed slightly later than usual.

I also wanted to avoid having to implement complicated logic in every automation that needed to behave differently during night mode.

Helpers - An Abstraction for Complex Logic

In software development, we typically hide complex logic behind abstraction and provide a simplified interface that other parts of our code can interact with – this allows us to manage complexity more effectively without getting bogged down by the implementation details. This separation of concerns makes code easier to understand, maintain, and modify, promoting reusability and reducing the likelihood of errors.

The same process of abstraction can be applied to home automation. With Home Assistant, I can use a helper and a series of automation to provide an abstraction for more complicated logic.

Let's consider a realistic "night mode" example where I want Home Assistant to determine if we have gone to bed and to behave accordingly by modifying the behaviour of specific automations.

The high-level requirements for determining if we're in night mode may be as follows:

  • The time must be between 8 pm and 6 am,
  • Alice and James must be at home with their phones plugged in to charge and in the bedroom
  • If either Alice or James is not at home, then night mode should still work based on who is at home and the charging state/location of their phone

We could implement this logic into every automation that needed to be aware of "night mode", but this would be tedious, and changing or modifying the rules for determining if we're in "night mode" would be a maintenance nightmare.

The alternative is to use an abstraction in the form of a helper and some dedicated automation:

  • "Night Mode - Enabled" Automation
    This automation will be used to determine if we should be operating in night mode.
  • "Night Mode - Disabled" Automation
    This automation will be used to determine if we are no longer in night mode.
  • "night_mode" Boolean Helper
    This will represent the output of these two night mode automation – either true or false.

The benefit of this approach is that we can hide the details of what constitutes "night mode" in the "Night Mode - Enabled" and "Night Mode - Disabled" automation. Because the output of these automations is encapsulated in the "nigh_mode" boolean helper it means that:

  • If we ever need to change the definition of "night mode", it will at most affect the "Night Mode - Enabled" and "Night Mode - Disabled" automations, as these are responsible for determining if we are in night mode or not.
  • When we implement night mode in individual automations we only have to look at a the simplistic state of the "night_mode" boolean. Individual automations do not have to worry about the implementation detail of what behaviours and triggers constitute night mode.

After all, if we consider the logic behind the high-level requirements that we outlined earlier, it manifests itself into a nested series of AND and OR logical conditions:

Logic diagram for conditions required to enter night mode
Logic diagram for conditions required to enter night mode

Building The Abstraction

The first step is to create the Input Boolean helper that will be used to store if we are in Night Mode or not.

  1. Navigate to Settings
  2. Select Device & services
  3. Click on the Helpers tab.
  4. Click on + Create Helper and select Toggle
  5. Set the name of your toggle to "Night Mode".
Creating a toggle helper for Night Mode in Home Assistant

We will now need to implement an automation that is responsible for turning on Night Mode under the correct conditions. The following automation replicates the behaviour in the above logic diagram.

alias: Night Mode - Enable
description: Night Mode - Enable
mode: single
trigger:
  # The automation should trigger for anything that is considered in the
  # conditions
  - platform: state
    entity_id:
      - person.james_ridgway
      - person.alice_ridgway
    to: home
  - platform: state
    entity_id:
      - sensor.jamess_iphone_battery_state
      - sensor.pixel_6a_battery_state
    to: Charging
  - platform: state
    entity_id:
      - sensor.james_iphone_ble
      - sensor.alice_phone_ble
    to: master_bedroom
condition:
  # We must be in the correct time window
  - condition: time
    after: "20:00:00"
    before: "05:00:00"
  # AND (james is away OR james is in the bedroom with phone on charge)
  - condition: and
    conditions:
      # OR
      - condition: or
        conditions:
          # james is away
          - condition: state
            entity_id: person.james_ridgway
            state: not_home
          # james is in the bedroom with phone on charge
          - condition: and
            conditions:
              - condition: state
                entity_id: person.james_ridgway
                state: home
              - condition: state
                entity_id: sensor.jamess_iphone_battery_state
                state: Charging
              - condition: state
                entity_id: sensor.james_iphone_ble
                state: master_bedroom
  # AND (alice is away OR alice is in the bedroom with phone on charge)
  - condition: and
    conditions:
      # OR
      - condition: or
        conditions:
          # alice is away
          - condition: state
            entity_id: person.alice_ridgway
            state: not_home
          # alice is in the bedroom with phone on charge
          - condition: and
            conditions:
              - condition: state
                entity_id: person.alice_ridgway
                state: home
              - condition: state
                entity_id: sensor.pixel_6a_battery_state
                state: charging
              - condition: state
                entity_id: sensor.alice_phone_ble
                state: master_bedroom
action:
  # Turn on night mode
  - action: input_boolean.turn_on
    metadata: {}
    data: {}
    target:
      entity_id: input_boolean.night_mode

Similarly, we will need another automation that is responsible for turning off Night Mode under the correct conditions:

alias: Night Mode - Disable
description: Night Mode - Disable
mode: single
trigger:
  # The automation should trigger for anything that is considered in the
  # conditions
  - platform: state
    entity_id:
      - person.james_ridgway
      - person.alice_ridgway
    to: home
  - platform: state
    entity_id:
      - sensor.jamess_iphone_battery_state
      - sensor.pixel_6a_battery_state
    to: Not Charging
  - platform: template
    value_template: "{{ states('sensor.james_iphone_ble') != 'master_bedroom' }}"
  - platform: template
    value_template: "{{ states('sensor.alice_phone_ble') != 'master_bedroom' }}"
condition:
  # We must be in the correct time window
  - condition: time
    after: "05:00:00"
    before: "20:00:00"
  # AND (james not home OR james is not in the bedroom and phone unplugged)
  - condition: and
    conditions:
      # OR
      - condition: or
        conditions:
          # james not home
          - condition: state
            entity_id: person.james_ridgway
            state: not_home
          - condition: and
            conditions:
              - condition: state
                entity_id: person.james_ridgway
                state: home
              - condition: state
                entity_id: sensor.jamess_iphone_battery_state
                state: Not Charging
              - condition: template
                value_template: "{{ states('sensor.james_iphone_ble') != 'master_bedroom' }}"
  # AND (alice not home OR alice is not in the bedroom and phone unplugged)
  - condition: and
    conditions:
      # OR
      - condition: or
        conditions:
          # alice not home
          - condition: state
            entity_id: person.alice_ridgway
            state: not_home
          # alice is not in the bedroom and phone unplugged
          - condition: and
            conditions:
              - condition: state
                entity_id: person.alice_ridgway
                state: home
              - condition: state
                entity_id: sensor.pixel_6a_battery_state
                state: Not Charging
              - condition: template
                value_template: "{{ states('sensor.alice_phone_ble') != 'master_bedroom' }}"
action:
  # Turn off night mode
  - action: input_boolean.turn_off
    metadata: {}
    data: {}
    target:
      entity_id: input_boolean.night_mode

Using Night Mode in Existing Automations

Using night mode in automation is now a simple task of checking the state of the Night Mode input boolean helper. We don't have to worry about the complex logic and rules that determine if we are in night mode – this has been contained in the two automation that toggles this input boolean helper on and off.

We have an automation for our living room that will turn the lamps on when motion is detected:

alias: Living Room Lamps - Motion Turn On
description: Turn on living room lamps when there is motion
trigger:
  - platform: state
    entity_id: binary_sensor.living_room_motion
    from: "off"
    to: "on"
condition:
  - condition: state
    entity_id: light.living_room_lights
    state: "off"
action:
  - service: light.turn_on
    target:
      entity_id: light.living_room_lamp_window
  - service: light.turn_on
    target:
      entity_id: light.living_room_lamp_back
mode: single

The living room is also where nappy changing takes place for our baby.

Before introducing night mode, you could go to change a nappy in the early hours of the morning, to be greeted by the lamps turning on automatically to 70% brightness – which can be quite bright and unwelcoming when you're stumbling around half asleep at 2 am.

We have now changed the automation, so that when we're in night mode, only one of the lamps is turned on, and it will turn on at 10% brightness rather than 70%:

alias: Living Room Lamps - Motion Turn On
description: Turn on living room lamps when there is motion
trigger:
  - platform: state
    entity_id: binary_sensor.living_room_motion
    from: "off"
    to: "on"
condition:
  - condition: or
    conditions:
      - condition: state
        entity_id: input_boolean.night_mode
        state: "on"
      - condition: state
        entity_id: light.living_room_lights
        state: "off"
action:
  - service: light.turn_on
    target:
      entity_id: light.living_room_lamp_back
    data_template:
      brightness_pct: >
        {% if is_state('input_boolean.night_mode', 'on') %}
          10
        {% else %}
          70
        {% endif %}
  - if:
      - condition: state
        entity_id: input_boolean.night_mode
        state: "off"
    then:
      - service: light.turn_on
        target:
          entity_id: light.living_room_lamp_window
        data_template:
          brightness_pct: 70
mode: single

Summary

Helpers can be used in conjunction with special-purpose automations to abstract away complex behaviour, where the helper is used to model the output of this more complex logic.

This allows us to define complex behaviour in one or two places and reuse that behaviour across many automations, therefore reducing the complexity of each individual automation and increasing the maintainability of our automations.