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:
- All the lights are off, we’re good then, stop execution
- 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
- 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.