Script Templating - turn off only lights that are on

I honestly don’t know. Haven’t used the script editor. Typically handle all my scripts through yaml.

I’ve tried this for a couple days now, and still get the same error when calling as a script. Starting to get a bald spot from scratching…

As far as I can tell it errors out whenever the result is greater than one entity. Anyone else using this bit of code still having luck with it? Running ver 91.4.

Traceback (most recent call last):
  File "/usr/src/app/homeassistant/core.py", line 1115, in async_call
    raise ServiceNotFound(domain, service) from None
homeassistant.exceptions.ServiceNotFound: (ServiceNotFound(...), 'Service [fan.turn_off not found')

Script 1:

s_light_off:
  sequence:
  - service: script.s_off
    data_template: 
      entity: >
        {% set domains = ['light','switch','fan','media_player'] %}
        {% set on_states = ['on'] %}
        {{ states | selectattr('domain','in', domains) | selectattr('state','in',on_states) | map(attribute='entity_id') | join('", "') }}

Script 2:

s_off:
  sequence:
  - service: homeassistant.turn_off
    data_template:
      entity_id: >
        ["{{ entity }}"]

Remove all those quotes you got in there. Not sure why you have them.

s_light_off:
  sequence:
  - service: script.s_off
    data_template: 
      entity: >
        {% set domains = ['light','switch','fan','media_player'] %}
        {% set on_states = ['on'] %}
        {{ states | selectattr('domain','in', domains) | selectattr('state','in',on_states) | map(attribute='entity_id') | join(', ') }}
s_off:
  sequence:
  - service: homeassistant.turn_off
    data_template:
      entity_id: "{{ entity }}"

I had tried that yesterday but received an error, which is why I went the bracket quote route to make it an array.

Using your example gives me the following error:

Traceback (most recent call last):
  File "/usr/src/app/homeassistant/core.py", line 1118, in async_call
    processed_data = handler.schema(service_data)
  File "/usr/local/lib/python3.7/site-packages/voluptuous/schema_builder.py", line 267, in __call__
    return self._compiled([], data)
  File "/usr/local/lib/python3.7/site-packages/voluptuous/schema_builder.py", line 589, in validate_dict
    return base_validate(path, iteritems(data), out)
  File "/usr/local/lib/python3.7/site-packages/voluptuous/schema_builder.py", line 427, in validate_mapping
    raise er.MultipleInvalid(errors)
voluptuous.error.MultipleInvalid: not a valid value for dictionary value @ data['entity_id']

It won’t actually make it into an array. It will keep it a string. So that method of piecing strings together won’t work.

For giggles, try placing 2 entities in that field with a comma. See if that errors and we’ll go from there.

s_off:
  sequence:
  - service: homeassistant.turn_off
    data_template:
      entity_id: light.1, light.2

Same error as post #45.

s_off:
  sequence:
  - service: homeassistant.turn_off
    data_template:
      entity_id: light.br2_light_level, light.living_room

what about this:

s_off:
  sequence:
  - service: homeassistant.turn_off
    data_template:
      entity_id:
      - light.br2_light_level
      - light.living_room

If this works, then I suspect that the method used to parse entity_id outside the data object is not used when entity_id is inside the data object. And because of this, this may never work. It’s odd because this used to work. Possibly a bug in the current version, possibly by design.

It worked.

I’ll look through PRs to see if there are issues opened.

I modified the python script that @amelchio posted to what I needed. I’m sure it can be improved upon.

"""Turn off devices that are on unless ignored."""

ignore = data.get("entities_ignore","fan.mbr_fan_level")
entities_ignore=ignore.split(",")

for entity_id in hass.states.entity_ids('light'):
    state = hass.states.get(entity_id)
    if state.state == 'on' and (entity_id not in entities_ignore):
        device.append(state.entity_id)

for entity_id in hass.states.entity_ids('fan'):
    state = hass.states.get(entity_id)
    if state.state == 'on' and (entity_id not in entities_ignore):
        device.append(state.entity_id)

if device:
    data = { "entity_id" : device }
    hass.services.call('homeassistant', 'turn_off', data)
1 Like

Eh, it looks like decent code. You could optimize it this way to get the same effect but it may not be worth the trouble unless you want to add more domains:

"""Turn off devices that are on unless ignored."""

ignore = data.get("entities_ignore","fan.mbr_fan_level").split(",")
domains = ['light','fan']
device = []

for domain in domains:
    for entity_id in hass.states.entity_ids(domain):
        state = hass.states.get(entity_id)
        if state.state == 'on' and entity_id not in ignore:
            device.append(state.entity_id)

if device:
    data = { "entity_id" : device }
    hass.services.call('homeassistant', 'turn_off', data)
1 Like

Thanks!! I was kinda hoping you would. I’m a complete novice in python.

So we need to have it look like this, right?

s_off:
  sequence:
  - service: homeassistant.turn_off
    data_template:
      entity_id:
      - light.br2_light_level
      - light.living_room

I think this may do it…

'turn_things_off_when_away_daytime':
  alias: Turn Things Off When Away Daytime
  sequence:
  - service: homeassistant.turn_off
    data_template:
      entity_id: >
        - {% set domains = ['light','switch','fan','media_player'] -%}
         {% set on_states = ['on'] -%}
      {{ states | selectattr('domain','in', domains) | selectattr('state','in',on_states) | map(attribute='entity_id') | join('\n- ') }}

Nope, you can’t template across fields in Jinja. Jinja only outputs to 1 field. Each - is a new field.

Well that stinks. I tried doing the python script the way Walrus_Parka did it but I guess I don’t understand how to call it.

I have a folder in my config called python_scripts and inside there I have lights_off.py with this in it:

"""Turn off devices that are on unless ignored."""

ignore = data.get("entities_ignore","fan.mbr_fan_level").split(",")
domains = ['light','fan','switch']
device = []

for domain in domains:
    for entity_id in hass.states.entity_ids(domain):
        state = hass.states.get(entity_id)
        if state.state == 'on' and entity_id not in ignore:
            device.append(state.entity_id)

if device:
    data = { "entity_id" : device }
    hass.services.call('homeassistant', 'turn_off', data)

How can I call that in a script and in an Automation?

1 Like

its a python script, follow the directions here for installation.

After that, look in your services tab for the python script, and use that service call as the service in your action section of a automation.

2 Likes

I was missing the “python_script:” in my config file. Working perfectly now. Thanks for the super quick and helpful replies!

1 Like

Ok…so I want to see if we can take this one step further. Currently we have a hard coded list that is ignored:

"""Turn off devices that are on unless ignored."""

ignore = data.get("entities_ignore","fan.mbr_fan_level").split(",")
domains = ['light','fan','switch']
device = []

for domain in domains:
    for entity_id in hass.states.entity_ids(domain):
        state = hass.states.get(entity_id)
        if state.state == 'on' and entity_id not in ignore:
            device.append(state.entity_id)

if device:
    data = { "entity_id" : device }
    hass.services.call('homeassistant', 'turn_off', data)

Is it possible to have that be a variable? And then when we call the python_script from an automation or a script we pass the list that needs to be ignored in the json? Something like this:

image

colon is wrong. You got it inside the quotes, needs to be outside the quotes between the 2 strings.

{
"blah": "blah",
}

That got rid of the error. But do we need to change this to accept that?

ignore = data.get("entities_ignore","fan.mbr_fan_level").split(",")

don’t believe so. The default is ‘fan.mbr_fan_level’ if ‘entities_ignore’ is empty or not supplied.