Turn on light, switch, scene, script or group based on motion and illuminance (+ more conditions)

Tags: #<Tag:0x00007fc403ef0050> #<Tag:0x00007fc403ef75f8> #<Tag:0x00007fc403ef7490> #<Tag:0x00007fc403ef71e8> #<Tag:0x00007fc403ef6ec8>

Github Gist: https://gist.github.com/freakshock88/2311759ba64f929f6affad4c0a67110b

Importing

Click the badge to import this Blueprint: (needs Home Assistant Core 2021.3 or higher)

Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.

Or import this Blueprint by using the Gist URL.

Main features

Turn on a light, switch, scene or script based on motion detection, and (optionally) low illuminance level.
Also you are able to set a blocker entity, and time limits for the automation.
Optionally, you can have the entity turn off after a certain amount of minutes of no motion detection.

Prerequisites

Some of the (optional) input values in this blueprint use helper entities you have to create yourself, to be able to dynamically set limits. It is not possible to set hardcoded limits for these inputs.

Optional features:

Is is also possible to define a blocking entity, which blocks the automation from running when this entity’s state is on. This could be for instance that sleep mode is enabled.

Time limits can also be defined to limit the time before and after the automation should trigger.
Outside of these limits the automation should not trigger.

If you do not configure the optional entities the automation will skip these conditions.

Optional entities:
- Illuminance sensor (sensor)
- Illuminance cutoff value (input_number)
- Blocking entity (any entity with state on/off)
- Time limit before (input_datetime)
- Time limit after (input_datetime)
- Wait time (input_number)

Defining optional entities in YAML: example code:

input_number:
  illumination_bedroom_low_cutoff:
    name: 'Illumination bedroom low cutoff value'
    min: 1
    max: 100
    step: 1
  bedroom_no_motion_turn_off_delay:
    name: 'Time in minutes automation waits to turn off bedroom lights after no motion'
    min: 1
    max: 120
input_boolean:
  sleepmode:
    name: 'Sleepmode'
input_datetime:
  bedroom_lights_turn_on_before_limit:
    name: 'Bedroom lights no longer turn on before this time'
    has_date: false
    has_time: true
  bedroom_lights_turn_on_after_limit:
    name: 'Bedroom lights no longer turn on after this time'
    has_date: false
    has_time: true

Defining optional entities in the UI

If you want to create the optional entities in the UI instead of in YAML, you van do so in the following way:

  • To to Configuration
  • Go to Helpers
  • Click the ‘Add helper’ button
  • Select the entity type (in this case, number, toggle or date/time)
  • Enter required fields
  • Click on create

Setting the values for the helper entities

To set a value for your newly created helper, find it in the helper list as described in the section above, press the settings icon on the top right of the screen and set the value there.

Blueprint Code

blueprint:
  name: Turn on light, switch, scene, script or group based on motion and illuminance.
  description: >
    Turn on a light, switch, scene, script or group based on motion detection, and low light level.

    This blueprint uses helper entities you have to create yourself for some input values, to be able to dynamically set limits. 
    For instructions on creating the helper entities take a look in the Home Assistant Community forum topic:
    https://community.home-assistant.io/t/turn-on-light-switch-scene-or-script-based-on-motion-and-illuminance-more-conditions/257085


    Required entities:
      - Motion sensor (single sensor or group)
      - Target entity (light, switch, scene or script)

    
    Optional features:

    - You can set a cutoff entity of which the value determines whether the illuminance level is low and the automation needs to trigger.
    - You can define a blocking entity, which blocks the automation from running when this entity's state is on.
    - You van define a turn-off blocking entity, which blocks the entity from turning off after the set delay.
    - Time limits can also be defined to limit the time before and after the automation should trigger.
    - If you want the entity to turn off after a certain amount of minutes, you can use the Wait Time input.
    - If you want another entity than the target_entity to turn off after the delay, you can define a separate Turn-off entity.
    - If you do not enable the optional entities the automation will skip these conditions.



    Optional entities:

    - Illuminance sensor (sensor in illuminance class)

    - Illuminance cutoff value  (input_number)
    
    - Blocking entity (any entity with state on/off)

    - Time limit before (input_datetime)

    - Time limit after (input_datetime)

    - Turn off wait time [in minutes!] (input_number) - will not work with script or scene target entities.

    - Turn off entity (any entity_id)

  domain: automation
  input:
    motion_sensor:
      name: Motion Sensor
      description: This sensor will trigger the turning on of the target entity.
      selector:
        entity:
    target_entity:
      name: Target entity.
      description: The light, switch, scene to turn on (or script to run) when the automation is triggered.
      selector:
        entity:
    illuminance_sensor:
      name: (OPTIONAL) Illuminance sensor
      description: This sensor will be used to determine the illumination.
      default:
      selector:
        entity:
          domain: sensor
          device_class: illuminance
    illuminance_cutoff:
      name: (OPTIONAL) Illuminance cutoff value
      description: This input_number will be used to compare to the current illumination to determine if it is low.
      default:
      selector:
        entity:
          domain: input_number
    blocker_entity:
      name: (OPTIONAL) Blocking entity
      description: If this entity's state is on, it will prevent the automation from running. E.g. sleepmode or away mode.
      default:
      selector:
        entity:
    time_limit_after:
      name: (OPTIONAL) Only run after time.
      description: Automation will only run when time is later than this input_datetime value.
      default:
      selector:
        entity:
          domain: input_datetime
    time_limit_before:
      name: (OPTIONAL) Only run before time.
      description: Automation will only run when time is earlier than this input_datetime value.
      default:
      selector:
        entity:
          domain: input_datetime
    no_motion_wait:
      name: (OPTIONAL) Turn off wait time (minutes)
      description: Time in minutes to leave the target entity on after last motion is detected. If not used entity will not auto turn off.
      default:
      selector:
        entity:
          domain: input_number
    turn_off_blocker_entity:
      name: (OPTIONAL) Turn-off Blocking entity
      description: If this entity's state is on, it will prevent the target entity from turning off after the set delay.
      default:
      selector:
        entity:
    target_off_entity:
      name: (OPTIONAL) Turn-off entity
      description: If defined, this entity will be turned off instead of the default target entity. This can be helpful when using target entities of type scene or script.
      default:
      selector:
        entity:

mode: restart
max_exceeded: silent

variables:
  target_entity: !input target_entity
  illuminance_currently: !input illuminance_sensor
  illuminance_cutoff: !input illuminance_cutoff
  blocker_entity: !input blocker_entity
  time_limit_before: !input time_limit_before
  time_limit_after: !input time_limit_after
  no_motion_wait: !input no_motion_wait
  entity_domain: "{{ states[target_entity].domain }}"
  turn_off_blocker_entity: !input turn_off_blocker_entity
  target_off_entity: !input target_off_entity

trigger:
  platform: state
  entity_id: !input motion_sensor
  to: 'on'

condition:
# First condition: When entity was already on because the automation ran recently, do not check illuminance because it could have increased above threshold because of a light that was just turned on.
  - condition: template
    value_template: "{{ (states[target_entity].state == 'on') or (illuminance_currently == none) or (illuminance_cutoff == none) or (states[illuminance_currently].state | int < states[illuminance_cutoff].state | int) }}"
  - condition: template
    value_template: "{{ (blocker_entity == none) or (states[blocker_entity].state == 'off') }}"
  - condition: template
    value_template: >
      {% set current_time = now().strftime("%H:%M")  %}

      {% if time_limit_before == none and time_limit_after == none %}
      true
      {% endif %}

      {% if time_limit_before != none and time_limit_after == none %}
      {% set current_time_is_before_limit = current_time < states[time_limit_before].state  %}
      {{ current_time_is_before_limit }}
      {% elif time_limit_before == none and time_limit_after != none %}
      {% set current_time_is_after_limit = current_time > states[time_limit_after].state  %}
      {{ current_time_is_after_limit }}
      {% endif %}

      {% if time_limit_before != none and time_limit_after != none %}
      {% set before_limit_is_tomorrow = states[time_limit_before].state < states[time_limit_after].state  %}
      {% set current_time_is_before_limit = current_time < states[time_limit_before].state  %}
      {% set current_time_is_after_limit = current_time > states[time_limit_after].state  %}
      {% set time_window_spans_midnight = states[time_limit_after].state > states[time_limit_before].state  %}
        {% if time_window_spans_midnight != none and time_window_spans_midnight and before_limit_is_tomorrow %}
        {{ current_time_is_after_limit or current_time_is_before_limit }}
        {% elif time_window_spans_midnight != none and not time_window_spans_midnight %}
        {{ current_time_is_before_limit and current_time_is_after_limit }}
        {% endif %}
      {% endif %}

action:
  - service: homeassistant.turn_on
    entity_id: !input target_entity
  - condition: template
    value_template: "{{ no_motion_wait != none }}"
  - wait_for_trigger:
      platform: state
      entity_id: !input motion_sensor
      from: "on"
      to: "off"
  - delay: 
      minutes: '{{ states[no_motion_wait].state | int }}'
  - condition: template
    value_template: "{{ (turn_off_blocker_entity == none) or (states[turn_off_blocker_entity].state == 'off') }}"
  - choose:
    - conditions:
        - condition: template
          value_template: "{{ (target_off_entity != none) }}"
      sequence:
        - service: homeassistant.turn_off
          entity_id: !input target_off_entity
    default:
      - service: homeassistant.turn_off
        entity_id: !input target_entity

Changelog

  • 2020-12-18: Initial version
  • 2020-12-18.1: Fix for hardcoded time limits
  • 2020-12-18.2: Refactored templates to be shorter, thanks to @123
  • 2020-12-19: Simplified action template
  • 2020-12-19.1: Added optional input to turn off entity after a certain amount of minutes of no motion
  • 2020-12-20: Added group support, fixed time condition bug
  • 2020-12-20.1: Made illumination sensor and cutoff value optional.
  • 2020-12-22: Removed entity state ‘on’ condition and set automation mode to restart to allow for entity off timer restart.
  • 2020-12-24: Added skip illuminance check if entity just turned on (when automation was restarted)
  • 2020-12-28: Added turn off blocking entity.
  • 2021-01-03: Fixed bug where time_limit_before did not work correctly when it was on the next day.
  • 2021-01-03.1: Added possibility to define turn-off entity other than target_entity.
  • 2021-01-08: Removed restriction of motion sensor needing to be binary_sensor in motion class. Now also possible to use groups of motion sensors. Also improved time condition.
  • 2021-01-21: Swapped input fields for time after and before to be more logical
  • 2021-09-02: Rewrote time condition to possibly fix time issue with blueprint not triggering when time window spans midnight.

If you have any issues or questions, please let me know :slight_smile:

26 Likes

The time limits are inputs so why are they hard-coded in the template?

  - condition: template
    value_template: >
      {% set time_limit_after = '14:00'  %}
      {% set time_limit_before = '23:00'  %}

Is that a leftover from a previous version?

1 Like

Very nice, this was exactly what i was starting to build and seen the new Blueprint option so I thought id look. Very nice!

Question though. I have DIY ESPHome ESP8266 sensors setup. For instance im doing my bathroom. For one it didnt detect my illuminance sensors, so i manually entered the id. I created a helper for the Bathroom Lux and selected it. Not sure how thats working though. My main question is for the blocker. I have a Humidity sensor that I want to use. Basically I want to to not shut the lights off if the humidity is high (Someone in the shower) but since its not boolean im not sure how your blueprint will handle it. Maybe a template of some form?

Good catch, that was some leftover testing template code indeed. I just fixed it in the gist and post itself.

As for your main question: I use the trend sensor to detect if humidity is increasing in my bathroom. This results in a binary_sensor, which is a boolean value. You could use this value as a blocker entity in this blueprint if you want :slight_smile:

I’m not sure if I undertstand the first part of your post correctly, but if you need any more help let me know.

Many of the templates aren’t taking full advantage of what Jinja2 offers.

For example, this:

  - condition: template
    value_template: >
      {% if entity_domain == "scene" %}
      true
      {% elif states[target_entity].state == 'off' %}
      true
      {% else %}
      false
      {% endif %}

can be reduced to this:

  - condition: template
    value_template: >
      {{ entity_domain == 'scene' or states[target_entity].state == 'off' }}

This:

  - condition: template
    value_template: >
      {% if blocker_entity == none %}
      true
      {% elif blocker_entity != none and states[blocker_entity].state == 'off' %}
      true
      {% elif blocker_entity != none and states[blocker_entity].state == 'on' %}
      false
      {% endif %}

can be expressed like this:

  - condition: template
    value_template: >
      {{ blocker_entity == none or states[blocker_entity].state == 'off' }}

I assume all of the following is to check if the current time is within the specified time range.

  - condition: template
    value_template: >
      {% set before_timestamp = '23:59'  %}
      {% set after_timestamp = '00:00'  %}

      {% if time_limit_before == none and time_limit_after == none %}
      true
      {% endif %}
      {% if time_limit_before != none %}
      {% set before_timestamp = time_limit_before %}
      {% endif %}

      {% if time_limit_after != none %}
      {% set after_timestamp = time_limit_after %}
      {% endif %}

      {% set current_time = now().strftime("%H:%M")  %}
      {% set current_time_before_limit = current_time < before_timestamp  %}
      {% set current_time_after_limit = current_time > after_timestamp  %}

      {% if current_time_before_limit and current_time_after_limit  %}
      true
      {% elif time_limit_before != none and current_time_before_limit and time_limit_after == none %}
      true
      {% elif time_limit_after != none and current_time_after_limit and time_limit_before == none %}
      true
      {% else %}
      false
      {% endif %}

It can be reduced to this:

  - condition: template
    value_template: >
      {{ (time_limit_before == none and time_limit_after == none) or 
         (time_limit_before != none and time_limit_after != none and  
          time_limit_after < now().strftime("%H:%M") < time_limit_before) }}

This:

  - service: >
        {% if entity_domain == "switch" %}
        switch.turn_on
        {% elif entity_domain == "light" %}
        light.turn_on
        {% elif entity_domain == "script" %}
        {{ target_entity }}
        {% elif entity_domain == "scene" %}
        scene.turn_on
        {% endif %}
    entity_id: !input target_entity

can be reduced to this:

  - service: "{{entity_domain}}.turn_on"
    entity_id: !input target_entity

If you wish, you can also take advantage of writing Template Conditions in shorthand notation.

9 Likes

Thanks for taking the effort to refactor some of my template code, I’ll incorporate it in a new version soon :slight_smile:

I just used your suggestions to refactor the templates.
I had to change some a bit to get them working, but the template code is much shorter now :wink: Thanks!

In the time limit one I have to handle the fact that there could be just a single time limit, before OR after. Also, in the first template I needed to add braces to each separate condition for it to work.
Your last suggestion was not entirely correct, because when a script is being called, there is no turn_on action, but the script is called by the name of the script itself.

Everything should still be working, I did some tests :slight_smile:

1 Like

Couldnt really get it working so i went ahead and created it manually. But i’ll redownload it again and test it out next time someone takes a shower and see if it does lux and humidity ok.

Also, I manually created my helper. I havent looked at your code yet but i may have that wrong which might be why. Thanks again man!!

How certain are you of this statement?

You may want to double-check.

Appears that motion sensor is required, even though it’s not specified as required in the docs.

Also, do you plan on adding any sort of ‘once a day’ condition? Or conditions on other things like a group being active?

1 Like

can you add an option to add a timer after which the light e.g. turns off…
btw many thanks for the blueprint!

You are correct. I guess I didn’t test properly that script.turn_on also works.
I was confused because calling script.scriptname also works, without any entity id.
Fixed it in the blueprint now.

I see that the docs were confusing there, I fixed that part now.
The motion sensor is indeed required, because that is the only trigger for this blueprint.

What is your usecase for a once a day condition? Since this blueprint is already quite complicated I want to know if it’s worth complicating it further.

I will look into group support later. Perhaps a group can also be a target entity (and also the condition to check whether the group is already on or not).

1 Like

I’ll look into that later as well. Im not sure whether to include a turn off action in this same blueprint or create a separate one for the turn off action. I’'ll post as soon as I have something.

1 Like

I have just updated the blueprint to have an optional input_number parameter which you can use to turn off the entity after x minutes of no more motion detected. Be aware that this only works for lights and switches, not for scenes or scripts.

Indeed it does because a script can be called two ways:

- service: script.turn_on
  entity_id: script.whatever

or

- service: script.whatever

The two methods are not 100% identical. The first method does not wait for the called script to finish; the caller continues to execute other actions while the script is executing. The second method waits for the called script to finish before proceeding to execute other actions.

For more information, see: Waiting for a script to complete

Where you added parentheses, such as the first template, was not needed. The statements were not ambiguous so the addition of parentheses doesn’t clarify anything. If you think they help you understand the template better, fine, but they’re not required for the Jinja2 interpreter.

In that case, the assumption I had made (both times are always use to define a range) was mistaken. The modification you made now handles it the desired way (and remains a fraction of the size of the original version). Good job!

1 Like

I just updated the blueprint again.
The time condidtion had a bug which I fixed.
Please update the blueprint if you are using it.
I also added groups as a possible target entity :slight_smile:

1 Like

Why does the action use a

condition: or

when there’s only one choice?

An oversight, fixed now.