Consolidating Automations

Hey fellow HA users! I just wanted to check and see how everyone else has approached this configuration.

Do you like having automations consolidated using templating, or do you like to keep each room as an individual automation? Example, do you have a different automation for motion in the living room and dining room, or do you like having them in a single one if they’re all related to turning on a light?

Currently I have as many consolidated as I can - One for motion detected, one for occupancy, one for manual switch presses, etc, but this can easily complicate it if I’m trying to troubleshoot a single room.

I never combine automations for anything unless it’s fairly simple, it makes sense to do it and it doesn’t complicate troubleshooting.

1 Like

If the action is different, or the conditions are different, then I usually make a separate automation. If multiple triggers do the same thing I usually combine them in one automatio so I can easily see which things do that particular thing.

For very simple automation where I can for instance easily template on/off then I will, but not if I need “if trigger is x do this else do that”, or if I need to test for triggers to see what conditions apply.

If I need multiple automations to do the same multi step action, I’ll break the actions out in a script.

I avoid wait for triggers for anything that takes longer than a few seconds. They too get their own automation.

That way I have very few long, complex automations. If anything misbehaves it is quite easy to find and fix.

1 Like

Hello famousjs,

There is no wrong way,

(Unless you are trying to enable and disable a bunch of automations or expecting your automations to run for more that a few minutes that is, IMO.)

What makes the most sense to you and you feel comfortable maintaining, do that.

As SG said, there’s no right or wrong way… as long as you can keep track of it. It’s also going to depend on your house’s layout, the type and quantity of devices you are using, and how you and your family utilize different parts of the house.

I have a pretty even mix of single-purpose automations and ones with significant branching and templating, but I generally keep any given automation to one contiguous region and one general concept. For example, our dining room, kitchen, and pantry areas form an L-shape region so I do all their lighting together in one automation because it can be annoying if lights are turning on and off repeatedly when we’re working near the border between areas.

One thing that can be a common source of frustration with consolidated/complex automations is that people don’t think about the edge cases where multiple branches may need to run at or near the same time. When consolidating automations it’s important to spend a little more time thinking about the automation mode to make sure that one mode will work for all the cases.

2 Likes

I have two homes and strive to keep the automations / templates identical. With each house, I strive to keep automations / templates identical (for example light control.) I also want to be able to make a change once and have it apply everywhere. Having these as individual automations make it much easier to look at trace logs when something doesn’t work.

So currently I have the package defined externally to HA. Run shell script to substitute the correct entities in and create a new package. Blueprints could also be used for a similar approach but they do not support templates (helpers), and I find most automations also require template, input numbers, etc. in summary one master package gets replicated out into dozens of packages across multiple HA systems.

These are great! I started off really trying as much to consolidate my automations, but like may here mentioned often times when i need to troubleshoot something it’s harder to do that. For example, my setup I have each room with an “_occupied” helper. (i.e. input_boolean.living_room_occupied).

I have an automation that detects presence, such as motion, or the TV turns on (apple TV) or a bed sensor has detection, and it will mark the room as occupied. A second automation will turn on the light if and when a room becomes “occupied.” This way if a motion detector thinks there is no one there, it won’t turn off the light unless it considers the room unoccupied. Same thing for a manual light switch press. a “input_boolean.living_room_manual” makes it so if a light switch gets pressed manually, it turns that on and nothing will turn it off unless the switch is hit again.

For the occupancy automation, the triggers are all of my motion sensors for every room (in one automation). If detected, it uses the following template to turn on an input_boolean:

entity_id: input_boolean.{{area_name(trigger.entity_id)}}_occupied

Turning off based on the sensors in my house, it has a condition:

{{ 
states('input_boolean.' ~ area_name(trigger.entity_id) ~ '_manual') in ['off'] 
and 
states('media_player.' ~ area_name(trigger.entity_id) ~ '_apple_tv') in ['standby', 'off', 'unknown']
and
states('binary_sensor.' ~ area_name(trigger.entity_id) ~ '_motion_sensor') in ['off', 'unknown']
and
states('input_boolean.' ~ area_name(trigger.entity_id) ~ '_sleep') in ['off', 'unknown']
and 
(
(expand(state_attr("input_select." ~ area_name(trigger.entity_id) ~ "_connected", "options")) | map(attribute='last_changed') | reject("<", now() - timedelta(minutes=2)) | list | length > 0) 
)
}}

That way it only considers the room unoccupied if everything is off that could be off, even if that room doesn’t have that sensor. The last part of that is what I’ve been messing aroudn with to reduce false positives if I really haven’t left the room. It only turns it off if a connected room (a input_select with a list of adjacent rooms) has detected motion or been occupied within the last 2 minutes, or an adjacent room is currently occupied.

This does make for a pretty satisfying setup, since every device has a configured area, but as you can see it really does complicate things if one room really isn’t acting the way it should be. I really only have 4 or so automations with it set up this way and I can just add a new sensor to the one automation and the rest of the manual and occupancy automations function as normal.

I was just curious to see how others had it set up.

1 Like

This is my full automation. It has helped eliminate almost all false positives. Lights can no longer turn on in rooms where no one is present, and lights don’t turn off if someone hasn’t left the room.

How this works - There is a room_map which identifies adjacent rooms. The automation checks to see if motion is no longer detected in a room, and has there been motion in an adjacent room, meaning you have left this room and moved on to the next. If there hasn’t then you’re almost certainly still in the same room and the motion sensor just stopped detecting you. If it has, then we can go ahead and mark the room as unoccupied. Additionally, if a room detects motion but no adjacent room has had recent motion, it’s almost certainly a false positive because you can’t “appear” in a room you would have had to come from an adjacent room.

I use some other sensors as indicators if the room is occupied or not such as a bed sensor or Apple TV.

There’s some door sensors as well since the first floor needs to know about its neighbors, and since its neighbor is “outside” we use the front and back door sensors as an adjacent indicator. This also works for rooms that don’t have an apple TV or bed sensor.

alias: Master Area Occupancy Manager (Strict Handoff + Gated Entry + Bed)
description: >-
  Handoff Logic: Requires Neighbor, Door, or Bed activity to turn ON.  Strict
  Neighbor/Bed check to turn OFF.
triggers:
  - trigger: state
    entity_id:
      - binary_sensor.lower_stairs_motion_sensor
      - binary_sensor.first_floor_motion_sensor
      - binary_sensor.hallway_motion_sensor
      - binary_sensor.laundry_room_motion_sensor
      - binary_sensor.master_bedroom_motion_sensor
      - binary_sensor.office_motion_sensor
      - binary_sensor.upper_stairs_motion_sensor
    to:
      - "off"
      - "on"
  - trigger: state
    entity_id:
      - binary_sensor.front_door_sensor_door
      - binary_sensor.back_door_sensor_door
    to: "on"
  - trigger: state
    entity_id:
      - media_player.master_bedroom_apple_tv
      - media_player.loft_apple_tv
    to: null
  - trigger: state
    entity_id: input_boolean.away
    to: "on"
  - trigger: state
    entity_id:
      - binary_sensor.master_bedroom_bed_sensor
    to:
      - "on"
      - "off"
actions:
  - choose:
      - conditions:
          - condition: template
            value_template: "{{ away_mode }}"
        sequence:
          - target:
              label_id: occupancy_controls
            action: input_boolean.turn_off
      - conditions:
          - condition: template
            value_template: >
              {# Basic Requirement: Motion, TV, or Bed, and not Away #}  {{
              (is_state('binary_sensor.' ~ area ~ '_motion_sensor', 'on') or
                  apple_tv_active or bed_occupied)
                  and not away_mode }}
          - condition: template
            value_template: >
              {# THE GATE: Only allow Turn On if... #} {# A. Room is ALREADY
              Occupied (Keep Alive) #}  {% if is_state('input_boolean.' ~ area ~
              '_occupied', 'on') %}
                {{ true }}
              {# B. Trusted Source: TV or Bed is Active #}  {% elif
              apple_tv_active or bed_occupied %}
                {{ true }}
              {# C. Valid Entry: Neighbor Handoff OR Exterior Door Entry #}  {%
              else %}
                {{ neighbor_occupied or door_active }}
              {% endif %}
        sequence:
          - target:
              entity_id: input_boolean.{{ area }}_occupied
            action: input_boolean.turn_on
      - conditions:
          - condition: template
            value_template: |
              {{ is_state('binary_sensor.' ~ area ~ '_motion_sensor', 'off')
                 and not apple_tv_active
                 and not bed_occupied
                 and not manual_mode
                 and not sleep_mode
                 and (neighbor_occupied or door_active) }} 
        sequence:
          - target:
              entity_id: input_boolean.{{ area }}_occupied
            action: input_boolean.turn_off
mode: parallel
max: 10
variables:
  area: >
    {% if trigger.entity_id == 'input_boolean.away' %}
      global
    {% elif trigger.entity_id in ['binary_sensor.front_door_sensor_door',
    'binary_sensor.back_door_sensor_door'] %}
      first_floor
    {% else %}
      {{ area_name(trigger.entity_id) | lower | replace(' ', '_') }}
    {% endif %}
  room_map:
    first_floor:
      - lower_stairs
      - powder_room
    powder_room:
      - first_floor
    lower_stairs:
      - first_floor
      - hallway
    hallway:
      - lower_stairs
      - laundry_room
      - master_bedroom
      - office
      - upper_stairs
    master_bedroom:
      - hallway
    master_bathroom:
      - master_bedroom
    office:
      - hallway
      - office_bathroom
    office_bathroom:
      - office
    laundry_room:
      - hallway
    upper_stairs:
      - hallway
      - loft
  door_map:
    first_floor:
      - binary_sensor.front_door_sensor_door
      - binary_sensor.back_door_sensor_door
  away_mode: "{{ is_state('input_boolean.away', 'on') }}"
  manual_mode: "{{ is_state('input_boolean.' ~ area ~ '_manual', 'on') }}"
  sleep_mode: "{{ is_state('input_boolean.' ~ area ~ '_sleep', 'on') }}"
  bed_occupied: >
    {% set bed_entity = 'binary_sensor.' ~ area ~ '_bed_sensor' %} {{
    is_state(bed_entity, 'on') if states(bed_entity) != 'unknown' else false }}
  apple_tv_active: >
    {% set tv_entity = 'media_player.' ~ area ~ '_apple_tv' %} {% if
    states(tv_entity) in ['unknown', 'unavailable'] %}
      {{ false }}
    {% else %}
      {{ states(tv_entity) in ['playing', 'buffering', 'on'] }}
    {% endif %}
  door_active: >
    {% set doors = door_map.get(area, []) %}  {% set ns =
    namespace(active=false) %}  {% for d in doors %}
      {% if is_state(d, 'on') %}
        {% set ns.active = true %}
      {% elif states[d] is defined and (as_timestamp(now()) - as_timestamp(states[d].last_changed)) < 120 %}
        {% set ns.active = true %}
      {% endif %}
    {% endfor %} {{ ns.active }}
  neighbor_occupied: >
    {% set neighbors = room_map.get(area, []) %}  {% set ns =
    namespace(active=false) %}  {% for n in neighbors %}
      {% set entity_id = 'input_boolean.' ~ n ~ '_occupied' %}
      {% if is_state(entity_id, 'on') %}
        {% set ns.active = true %}
      {% elif states[entity_id] is defined and (as_timestamp(now()) - as_timestamp(states[entity_id].last_changed)) < 60 %}
        {% set ns.active = true %}
      {% endif %}
    {% endfor %} {{ ns.active }}

1 Like