Template help: turn lights on in area if a sensor in the same area detects motion

So maybe this is a bit of an odd blueprint.
I have trouble creating a blueprint that triggers (motion == detected, ergo state is on) the lights that are in the same area as my motion just triggered sensor.

I have the following code to detect if any of my sensors are on, then get the area of that specific sensor and then use the area(s) in the lights.on service.

When I run this blueprint HASS keeps complaining it doesn’t get find following areas (the area names are exactly the same as my area names). These are:

  • living_room
  • office
  • hallway

This is my first time creating a blueprint and don’t know really how I can make this work.

Any advice on how to make this work?

Blueprint yaml:

blueprint:
  name: Turn on/off lights based on motion sensor
  description: Turn on or off lights based on which motion sensor has detected motion 
    or stopped detecting motion
  domain: automation
  input:
    motion_entity:
      name: Motion Sensor
      selector:
        entity:
          domain: binary_sensor
          device_class: motion
          multiple: true
    actions:
      name: Actions
      description: Action or similar to be run (e.g. turn on light). {{area}} is replaced with
        the area of where the sensors being triggered reside in.
      selector:
        action: {}
  source_url: https://input-url-here-later-on
variables:
  areas: |
    {% set result = namespace(area=[]) %}
    {% for state in states.binary_sensor 
      | rejectattr('attributes.device_class', 'undefined') 
      | selectattr('attributes.device_class', '==', 'motion') %}
      {%- if state.state == 'on' %}
        {## Get the area id so we know what area to turn light on ##}
        {% set result.area = result.area + [area_id(state.entity_id)] %}
      {% endif %}
    {% endfor %}
    {{result.area}}
trigger:
- platform: state
  entity_id: !input 'motion_entity'
  from: "off"
  to: "off"
condition: []
action:
- choose: []
  default: !input 'actions'
- service: light.turn_on
  data: {}
  target:
    area_id: |
      {%- if areas|length > 1 -%}
        {%- for area in areas -%}
        - {{area}}
        {% endfor %}
      {%- else -%}
        - {{areas}}
      {%- endif -%}
mode: single
- service: light.turn_on
  data: {}
  target:
    area_id: >
      {{ states.binary_sensor | selectattr('attributes.device_class', 'defined') 
        | selectattr('attributes.device_class', '==', 'motion')
        | selectattr('state', 'eq', 'on') | map(attribute='entity_id')
        | map('area_id') | unique | reject('none') | list }}

The template I posted above resolves the original problem you reported:

The error message you posted is complaining about a different problem:

2022-10-28 14:22:19.168 ERROR (MainThread) [homeassistant.components.automation] Blueprint Turn on/off lights based on motion sensor generated invalid automation with inputs OrderedDict([(‘motion_entity’, [‘binary_sensor.sensor3’])]): expected a dictionary for dictionary value @ data[‘variables’]. Got None

If you are using the template I suggested, your blueprint no longer needs the entire variables section so you can safely remove it. In addition, delete any existing automations produced by the blueprint and re-create them after you have executed Reload Automations.

Ah I’m sorry for the confusion. I figured that out and removed that, removed old automating and was typing a new reply which I didn’t send yet.

I get one step further but it now it thinks the output is incorrect:

2022-10-28 14:52:09.628 ERROR (MainThread) [homeassistant.components.automation.test_turn_on_off_lights_based_on_motion_sensor] TEST Turn on/off lights based on motion sensor: Error executing script. Invalid data for call_service at pos 1: template value should be a string @ data['area_id'][0]

My guess it requires the format:

- office
- hallway

Where this is the output in the template developer tool thingy:

[
  "hallway"
]

Does it misses the dash for the areas?

This is how you define a list in YAML:

- office
- hallway
- living_room

It’s equivalent to this:

["office", "hallway", "living_room"]

The error message indicates the value for area_id should be a string:

2022-10-28 14:52:09.628 ERROR (MainThread) [homeassistant.components.automation.test_turn_on_off_lights_based_on_motion_sensor] TEST Turn on/off lights based on motion sensor: Error executing script. Invalid data for call_service at pos 1: template value should be a string @ data[‘area_id’][0]

I have no idea why it claims it should be a string because area_id definitely accepts a list. It might be a software bug.


EDIT

target permits the use of device_id, entity_id, and area_id. All are designed to accept a list value.

As a comparative example, the following template produces a list of entity_ids. The service call works without producing an error message complaining it must be a string:

service: light.turn_off
data: {}
target:
  entity_id: >
    {{ states.light 
        | selectattr('state', 'eq', 'on')
        | map(attribute='entity_id') | list }}

In contrast, if we try to use a template to produce a list of area_ids we get an error message. That’s why I think it may be a bug.

Thanks again for everything! I really appreciate it

I converted my code to get the lights from the areas and when I use that in the call it works like a charm :).

This is my code so far:

      {% set areas = states.binary_sensor | selectattr('attributes.device_class', 'defined') 
        | selectattr('attributes.device_class', '==', 'motion')
        | selectattr('state', 'eq', 'on') | map(attribute='entity_id')
        | map('area_id') | unique | reject('none') | list %}
      {%- for area in areas -%}
        {{- states.light
          | selectattr('state', 'eq', 'off')
          | selectattr('entity_id', 'in', area_entities(area)) 
          | map(attribute='entity_id') | reject('none') | list }}
      {% endfor %}

Maybe not the prettiest code but it works.
And now to get the states from sensors I enter through a input field because I realised it also tries to find lights in areas where there are no lights so that errors out :stuck_out_tongue:

Did you test it in your blueprint? I can’t see how it can possibly work because it makes the same mistake as in your original example: it attempts to use a Jinja2 template to generate a list in YAML.

You can’t use a Jinja2 template to generate YAML. Home Assistant processes YAML first then processes Jinja2. Any Jinja2 template that generates YAML is too late because YAML processing comes before Jinja2 processing.

You are right. It seems to work some of the times but errors out that empty ids are found, so I think it’s because what you said.

Well then time to fix it again :frowning:

- service: light.turn_on
  data: {}
  target:
    entity_id: >
      {% set areas = states.binary_sensor | selectattr('attributes.device_class', 'defined') 
        | selectattr('attributes.device_class', '==', 'motion')
        | selectattr('state', 'eq', 'on') | map(attribute='entity_id')
        | map('area_id') | unique | reject('none') | list %}
      {% set ns = namespace(entities=[]) %}
      {% for x in areas %}
        {% set ns.entities = ns.entities + expand(area_entities(x) | select('match', 'light'))
          | selectattr('state', 'eq', 'off') | map(attribute='entity_id') | list %}
      {% endfor %}
      {{ ns.entities }}