How to chose entity (input_select), then define time frame, then shut down after that time frame?

Hi everybody,

I am trying to create a card that will let me chose from a list of entities, then allow me to set an amount of time, then shut down that entity.

Previously I had used a card that had two hard-coded entities, each with a hard-coded timer and input_select, but that was very limited; I’d have to create an options entry in input_select for each entity, as well as to create an individual input_select for (pre-defined) times for each entity.

Here is my new attempt, but I am already stuck…

group:
  to_shut_off:
    name: "timer test"
    entities:
      - switch.office_fan
      - switch.bedroom_tv
      - switch.livingroom_tv

input_select:
  off_timer:
    options:
      - dummy option 1
      - dummy option 2

automation:
  - id: ".."
    alias: ".."
    trigger: 
      - platform: time_pattern
        seconds: 10
    action:
      - service: input_select.set_options
        data_template:
          entity_id: input_select.off_timer
          options: |
            {% for each in expand('group.to_shut_off') %}
              {{ each.entity_id }}
            {% endfor %}

I expected this to create an input_select containing all entity_ids in the to_shut_off group. When I run the template from options in templates, it will do this.
(there is probably a much better way than doing this via time_pattern, but for testing purposes I thought this would be a quick way to check whether or not the automation worked at all; it didn’t, input_select kept the two dummy options)

Currently there are three main issues on my mind:

  1. how do I successfully create this input_select field that will let me pick one of the entities in the group?
  2. how do I create a timer (or instead of “counting down” just calculate the value, for example, if I set a timer for 30 minutes, don’t count down from 00:03:00, but rather just set a shutdown variable to now + 00:03:00; hope I explained this okay)
  3. how do I assure that multiple of these can run at the same time? For example, I’d like to be able to set a timer for entity 1 in 30 minutes, start it, and (perhaps 20 minutes later) set another timer for entity 2 for 90 minutes without the first timer to stop/disappear, so that entity 1 will shut of (in this case) ten minutes after I set the second timer and entity 2 will shut of 80 minutes after entity 1 (because when entity 1 turned off, 10 of those 90 minutes will have passed)

Thank you for your ideas :slight_smile:

For the first question, change to this:

          options: >
            {{ expand('group.to_shut_off')|map(attribute='entity_id')|join(',') }}

Basically the entity_id’s need to be separated with commas. Also, I’d trigger the automation with the homeassistant start event, unless you plan on dynamically changing the group contents.

Thank you. It did not work like this, so I tried the approach below. But then I’ll get homeassistant.exceptions.InvalidStateError: Invalid state encountered for entity id: input_select.test_geraete. State max length is 255 characters.

I used this template (based on yours) because I usually put each item in a new line with a prepended dash - {{ expand('group.abend')|map(attribute='entity_id')|join('\n- ') }}. The output looks correct:

- light.arbeitszimmer_led_fenster
- light.licht_arbeitszimmer_schreibtisch_01_light
- light.schlafzimmer_led_bett
- light.sz_led_seite
- switch.arb_fenster_relay_01
- switch.arb_fenster_relay_02
- switch.arbeitszimmer_drucker
- switch.arbeitszimmer_grun
- switch.arbeitszimmer_rechner
- switch.arbeitszimmer_ventilator
- switch.garage_licht
- switch.kuche_schranklicht
- switch.schlafzimmer_lichterkette
- switch.wohnzimmer_ventilator

This is not the actual group of devices I want to control, just using an already existing one for testing; however, if 255 characters is the limit, I won’t be able to use more than a handful of entities tops…

You can’t make it a YAML list by putting dashes in the string. Remember a template can only output a single string. Doesn’t matter what it looks like in the Template Editor. You’re just creating a single string that happens to contain dashes.

When you said what I suggested didn’t work, in what way? What was the error or the result?

Oh, thanks for letting me know. Yaml/jinja2 is still a bit confusing to me.

Well, usually the input_select would display those two dummy values; when I tried your solution, it did not contain anything at all; so upon (re)starting home assistant something must have happened and replaced the dummy values with an empty value (perhaps being over the 255 character limit).

This is the actual output when I manually run the automation with your provided line

2019-09-30 19:31:09 ERROR (MainThread) [homeassistant.components.automation] Error while executing automation automation.test_set_options. Unknown error for call_service at pos 1:
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/automation/__init__.py", line 434, in action
    await script_obj.async_run(variables, context)
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 162, in async_run
    await self._handle_action(action, variables, context)
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 246, in _handle_action
    await self._actions[_determine_action(action)](action, variables, context)
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 329, in _async_call_service
    context=context,
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 98, in async_call_from_config
    domain, service_name, service_data, blocking=blocking, context=context
  File "/usr/src/homeassistant/homeassistant/core.py", line 1234, in async_call
    await asyncio.shield(self._execute_service(handler, service_call))
  File "/usr/src/homeassistant/homeassistant/core.py", line 1259, in _execute_service
    await handler.func(service_call)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_component.py", line 213, in handle_service
    self._platforms.values(), func, call, service_name, required_features
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 349, in entity_service_call
    future.result()  # pop exception if have
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 371, in _handle_service_platform_call
    await getattr(entity, func)(**data)
  File "/usr/src/homeassistant/homeassistant/components/input_select/__init__.py", line 189, in async_set_options
    await self.async_update_ha_state()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 266, in async_update_ha_state
    self._async_write_ha_state()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 385, in _async_write_ha_state
    self.entity_id, state, attr, self.force_update, self._context
  File "/usr/src/homeassistant/homeassistant/core.py", line 986, in async_set
    state = State(entity_id, new_state, attributes, last_changed, None, context)
  File "/usr/src/homeassistant/homeassistant/core.py", line 731, in __init__
    ).format(entity_id)
homeassistant.exceptions.InvalidStateError: Invalid state encountered for entity id: input_select.test_geraete. State max length is 255 characters.

Note that the file will be parsed as YAML. This will result in the options parameter containing the template you entered. That template is not evaluated until HA is running and the service is called, and it will result in a single string. YAML parsing is history at this point, so you can’t create a list using dashes in the template output. That’s a YAML thing.

That’s a limit for an entity’s state. It’s not a limit to the options parameter of the input_select.set_options service, nor is it a limit for the input_select’s options attribute.

If the service call isn’t working then there should be something in the log to say why.

Please post the automation as it exists now.

My guess is you’re not using the template as I suggested. If you’ve replaced the commas with end-of-line and dashes, that would explain the problem. The entries must be separated by commas. Otherwise you’ll end up with just one, really long option (which will cause an error because it will try to set the current selection, which is the state, to that one option that will be too long.)

input_select:
  test_geraete:
    options:
      - hallo
      - okay

automation:
  - id: 'test_set_options'
    alias: "[TEST] Set Options"
    trigger:
      - platform: homeassistant
        event: start
    action:
      - service: input_select.set_options
        data_template:
          entity_id: input_select.test_geraete
          options: >
            {{ expand('group.abend')|map(attribute='entity_id')|join(',') }}

This is how I had it when triggering the automation which produced the output above.

And what does the output in the Template Editor look like if you enter:

{{ expand('group.abend')|map(attribute='entity_id')|join(',') }}

Oh, cr…, sh…, er, um, darn!

I just rechecked the code for the input_select.set_options service, and the schema for the options parameter uses cv.ensure_list instead of cv.ensure_list_csv!! This will never work. :angry:

Darn is right :grin:

Do you mean by “this” will never work that this method will not work, or that there is no way to create this timer package at all? If so: I would be fine hard-coding all entities (instead of creating the options from a group) if I could still set individual timers for them.

I guess the other option would be to continue manually create inputs for each entity I want to turn off like this, but that would be a lot of code for rather little result…

I mean that the way the service code is written you can’t set the input_select options via a single template. You’d have to modify the code from this:

SERVICE_SET_OPTIONS_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
    {
        vol.Required(ATTR_OPTIONS): vol.All(
            cv.ensure_list, vol.Length(min=1), [cv.string]
        )
    }
)

to this:

SERVICE_SET_OPTIONS_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
    {
        vol.Required(ATTR_OPTIONS): vol.All(
            cv.ensure_list_csv, vol.Length(min=1), [cv.string]
        )
    }
)

For the rest of what you’re trying to do I don’t really have reasonable suggestions. Honestly, if it were me, I’d try writing a custom integration.

I wish I could. I’d say I am an advanced beginner when it comes to python, so I know the basic stuff, but more advanced things like custom integrations are above my head. I will, however, look into it. Could imagine that this could be useful for other people than just myself as well.

(current use case is just to turn off the bedroom ceiling fan x minutes after going to bed because it is too warm at night but gets too cold in the morning when we leave it on… but I always prefer a solution that is ready for anything over just hard coding particular things)