One Light on at a Time

I have a 9-year old kid that tends to leave a trail of lights turned on wherever he goes. I created an automation that targets the rooms he alone uses pretty much exclusively. If he turns on a light in one room, leaves and turns on the light in another room, this automation turns off the light in the first room he turned on.
Each iteration will watch for activity for 2 hours before timing out.

blueprint:
  name: One Light on at a Time
  description: Keeps only a single light or switch in a group on at a time. If one light/switch is turned on while another is already on, turn the first one off.
  domain: automation
  input:
    light_list:
      name: Light List
      description: List of lights/switches to ensure only a single light or switch is on. Separate entity names with commas.
      selector:
        entity:
          domain: light
trigger:
  - platform: state
    entity_id: !input light_list
    from: 'off'
    to: 'on'
action:
  - wait_for_trigger:
      - platform: state
        entity_id: !input light_list
        from: 'off'
        to: 'on'
    timeout: '02:00:00'
    continue_on_timeout: false
  - condition: template
    value_template: '{{ wait.trigger.entity_id != trigger.entity_id }}'
  - service: homeassistant.turn_off
    data:
      entity_id: '{{ trigger.entity_id }}'
mode: parallel
max: 2
1 Like

Maybe I have misunderstood it but how can someone select multiple lights when the light_list selector only permits selecting a single light?

If the goal is to create a separate automation for each light, then the description should not indicate “list of lights”.

The ‘separate entity names with commas’ fails the moment you use the selector to choose a light (it overwrites whatever is written). In addition, the Save button is not present when you supply a comma-delimited list of lights. (Nope. Temporary glitch.)

I think there’s a missing part in regard of how to create the list of lights (shouldn’t there be individual lights/groups of lights or different setups based on the time of day/sun position?)

Also, what if someone is still in the room after the 2 hours have passed?

The dropdown doesn’t allow you to select multiple light entities, but it will allow you to type them in with comma-separated values. If there’s a better way to do this, I’m all ears!

I tried to use the same selector that is used in some of the samples, but it doesn’t seem to work with entity_id: in triggers. If that person is in the same room for more than 2 hours, then the automation will time out and not turn that light off when the next one is turned on. I considered not having the timeout, but thought it would be poor form to have automations running indefinitely.

Currently, there is no better way. It wasn’t designed to allow multiple selections.

Supplying a comma-delimited list works but has the disadvantage of obliging the user to remember the names of all the lights because they cannot be selected from the displayed menu.

To be clear, it’s a limitation of the Entity selector. Working around this limitation imposes an inconvenience for the user. Hopefully, the next version of the selector will permit multiple selections.

I wish this worked using the same method as the motion-activated lights example, but alas, it did not.

Interesting, I wasn’t aware the entity selector let you just type in a comma-separated list of values anyway.

I think you actually can work with that then, you just have to adjust the automation a bit with some templates. So first, you have to stuff the value of that input into a variable so you an access it in templates by adding this to the top of the automation:

variables:
  light_list: !input light_list

Next you won’t be able to use a state trigger, you’ll have to use a template trigger like this:

trigger:
- platform: template
  value_template: >-
    {{ (light_list | regex_replace('\\s+', '')).split(',') | expand | selectattr('state', 'eq', 'on') | list | count > 0 }}

So what this will do is strip all whitespace out of the users entry, split the list by commas, expand it to get the actual entity data and then return true if there are one or more lights on. It’s also going to trigger any time any of the lights referenced in light_list are updated.

Now unfortunately using a template trigger means you won’t get access to trigger.entity_id so we’re going to have to use essentially the same template again. Fortunately though this is going to trigger as soon as a light has turned on. So any time this triggers we want to immediately turn off all lights except the last one turned on like this:

action:
- service: light.turn_off
  data:
    entity_id: >-
      {{ ((light_list | regex_replace('\\s+', '')).split(',') | expand | selectattr('state', 'eq', 'on') | sort(reverse=True, attribute="last_changed") | map(attribute="entity_id") | list)[1:] }}

This first part of this template should look familiar since its reconstructing the same list of which lights from the list are on. Then its sorting that list based on last_changed to ensure the one turned on last is first, turning the list back into an array of light IDs and then stripping the first one off the list (since that’s the one you want to keep on).

If you just turn your automation into this trigger and this action that alone will ensure there is never more then one light in the list on at any one time. The two hour delay bit is the final part, for that we’ll these final 3 actions:

- wait_template: >-
    {{ (light_list | regex_replace('\\s+', '')).split(',') | expand | selectattr('state', 'eq', 'on') | list | count != 1 }}
  timeout: 
    hours: 2
- condition: template
  value_template: "{{ not wait.completed }}"
- service: light.turn_off
  data:
    entity_id: >-
      {{ (light_list | regex_replace('\\s+', '')).split(',') | expand | selectattr('state', 'eq', 'on') | map(attribute="entity_id") | first }}

You’ll also want to change the mode to queued so the next iteration of this automation cannot start until the previous one has finished (but then will be started, we don’t want to drop them).

So what happens now is that this will wait until the number of lights on from the list is not exactly 1 (since it just made it exactly one in the previous step) or two hours have passed. It will then proceed in one of the following ways:

  1. All the lights are off, we’re good then, stop execution
  2. More then one light is on, don’t do anything since another instance of this automation is now queued, just exit and let it run
  3. The wait timed out because the light was left on for 2 hours. In that case proceed and turn that light off by finding the one on one from the list

Phew, that was a lot. I really wish there was a “select list of entities” selector. But in the meantime, seems like this would work pretty efficiently using a comma-separated list form of entry.

[EDIT] Tweaked the trigger template to trigger any time any light turns on, not just more then one. Realized it didn’t handle the situation where a light turned on and you wanted it to turn back off 2 hours later. Also FYI these templates work fine if a user does just enter one light and wants it to timeout after 2 hours.

1 Like

Not sure the juice is worth the squeeze; despite all that heavy-lifting, the user still has to enter multiple light entities from memory avoiding typos (OK, you can peek at the menu … :slight_smile:).

A multi-select Entity selector is sorely needed.

Yeah, doesn’t all that just do the same thing that my automation already accomplishes with way less work?

Sorry I think I misunderstood. I thought you were saying the list of comma-separated IDs did not work in the trigger state area. You’re right then, ignore me, sorry about that.

Yea absolutely. There’s a few I think are really needed:

  1. List of entities selector
  2. Service selector, like provide the name of a service of type notify, scene, script or any of the types of services where you end up with a lot of unique ones that are called with a common interface. Notify in particular is problematic since there is no associated entity and the calls vary slightly from integration to integration, hence why you see everything selecting a device of type mobile_app right now
  3. Dictionary selector, so users can provide a dictionary that can then be used in templates in service calls

#1 is the biggest though

EDIT: One I forgot, a device by service selector (kind of an alternative to #2 but somewhat different). Like I want you to select a device which has a specific known service like turn_on, turn_off, notify, etc., regardless of what type of device it is. That would even be useful for this blueprint too, for instance, you can’t use this blueprint if you have switch entities instead of light entities for your lights. Perhaps because they are table lamps controlled by smart plugs or are lights represented as switches since they don’t have any dimming options.

It should be pretty easy for me to change it to allow for switch and lights to turn off. Just have to change light.turn_off to homeassistant.turn_off and update the selector, and it should be good to go!

Technically you are correct. But I’ll point out your selector is for light so a user would not see switches in the dropdown. However since apparently users can enter any text they want in an entity selector you are correct in that if they put in a comma-separated list of switches and lights and you changed the action to homeassistant.turn_off it would all work. But of course at that point the selector just isn’t doing anything at all, might as well just change it to text and put all this in the description.

If we’re making a wish list for Santa, this bastardized usage of choose needs a proper replacement:

- service: whatever
  entity_id: !input user_supplied_entity
- choose:
  default: !input user_supplied_actions

Currently, that’s the only way to insert a user-supplied set of actions among some existing actions. Ideally, something like a standalone sequence option would be permitted.

- service: whatever
  entity_id: !input user_supplied_entity
- sequence: !input user_supplied_actions

In other words, the ability to assign that !input directive to something other than a lobotomized choose.

I was able to add switch to the selector, so it will show all lights and switches. Tested and it works. Maybe Santa will give us a multi-select option for Christmas!

2 Likes

Congratulations! Well done! :+1:

That’s undocumented and the first example of a multi-domain Entity selector that I’ve seen in this forum.

FWIW, I just tried it in one of my blueprints and it works as advertised.


EDIT

I’ve discovered at least one limitation. If you add a qualifier for any of the domains (I added device_class: motion for domain: binary_sensor) the multiple domains are reported as an error (duplicated domain keys).

1 Like

Bad news. It appears you have discovered a bug as opposed to an undocumented feature.

When I use the blueprint to create an automation, the moment the Entity selector displays multiple domains, an error message is posted to Logs complaining about duplicate domain keys.

The bug is that neither Check Configuration or Reload Automations detects the duplicated domain keys. They’re caught at runtime when the Entity selector is used (or at least that’s how it appears based on my tests).

That’s odd. I did a test using a light and a switch and it accepted it in the automation. More importantly, the automation worked as expected.

I think if you check Logs you will find that it’s not entirely happy with it.

It might appear to be functioning properly but under the hood there’s a ticking sound …

The moment I select a light using an Entity selector listing lights and switches, this gets logged: