Help with data_template using dynamic entity names

Is it possible to interrogate the attributes of an entity by referring to a dynamic variable which is set to that entitles name?

I have a very script which is passed a data template containing two variables each with the name of a different input_boolean. I now know that the script receives them correctly (thanks again @anon43302295!!) and I can use the variables in a data_template as an entity_id.

I cannot however find a syntax which will allow me to interrogate the state of the input_boolean.

script:
  # Don't allow zone to be shown unless previous zone is shown
    
  # TEST MESSAGE #
  - service: persistent_notification.create
    data_template:
      message: |
        zone_toggled - {{ zone_toggled }}
        previous_zone - {{ previous_zone }}
        previous_zone state - {{ states('{{ previous_zone }}') }}
      title: "*** INFORMATION 1 ***"
  # TEST MESSAGE #
    
  - condition: template
    value_template: "{{ states('previous_zone') == 'off' }}"
  - service: homeassistant.turn_off
    data_template:
      entity_id: '{{ zone_toggled }}'

If I comment out this condition:

  - condition: template
    value_template: "{{ states('previous_zone') == 'off' }}"

This service runs so I know(!) everything except interrogating the state is working:

  - service: homeassistant.turn_off
    data_template:
      entity_id: '{{ zone_toggled }}'

image

Do you get an error? It should work like you have it I think.

Ooooh, hang on, sorry, are you saying about the `states(’{{previous_zone}}’) bit?

I don’t think that will work, you’ll have to send the state to the script from the calling one.

I think.

I know I had a similar problem with that, hence why my notification engine does some of the logic when it’s called, rather than being passed the logic (which I know is the other way around to your problem, but I think the cause is the same).

Thanks. I’ve been looking at your notification engine with relish for when I get to a stable point with what I have now!!

Passing the state sounds like a good direction to go. I’ll give it a try.
(In fact it might even facilitate solving another small logic issue I have!!)

Don’t use quotes around ‘previous_zone’

  - condition: template
    value_template: "{{ states('previous_zone') == 'off' }}"

the quotes make it a string. You don’t want that, you want to use it like an object:

value_template: "{{ states(previous_zone) == 'off' }}"

Also, this like looks wrong:

{{ states('{{ previous_zone }}') }}

should just need:

{{ states(previous_zone) }}
1 Like

I have a similair issue and can’t resolve, I tried several ways to use a variable enitity_id
These are my 2 scripts:

light_color_temp:
  sequence:
    - service: light.turn_on
      data_template:
        entity_id: "{{ entity_id }}"
        color_temp: >-
          {# original #}
          {# % set current = states.light.wohnzimmer_stehlampe.attributes.color_temp|default(0)|int % #}
          {# next has valid syntax and it works #}
          {% set current = state_attr("light.wohnzimmer_stehlampe", "color_temp")|default(0)|int %}
          {# next has valid syntax and it works #}
          {% set entity = "light.wohnzimmer_stehlampe" %}
          {% set current = state_attr(entity, 'color_temp')|default(0)|int %}
          {# ################################################## #}
          {# next syntax is not valid #}
          {# % set current = states.{{ entity_id }}.attributes.color_temp|default(0)|int % #}
          {# next syntax is not valid #}
          {# % set current = state_attr({{ entity_id }}, 'color_temp')|default(0)|int % #}
          {# next syntax is not valid #}
          {# % set entity = {{ entity_id }} % #}
          {# next has valid syntax, but it doesn't work, {{ entity_id }} is not assigned  #}
          {% set entity = "{{ entity_id }}" %}
          {# next has valid syntax and it works, but only with constant string, not with  #}
          {% set current = state_attr(entity, 'color_temp')|default(0)|int %}
          {# ################################################## #}
          {# wenn current = maximum, bei minimum weiter machen: #}
          {% if current == states('input_number.color_temp_max')|int %}
            {% set next = states('input_number.color_temp_min')|int %}
          {% else %}
            {% set step = states('input_number.color_temp_step')|int %}
            {% set next = current + step %}
            {% if next > states('input_number.color_temp_max')|int %}
              {% set next = states('input_number.color_temp_max')|int %}
            {% endif %}
          {% endif %}
          {{ next }}


light_color_temp_test:
  sequence:
  - service: script.light_color_temp
    data:
      entity_id: light.wohnzimmer_stehlampe

And a similair issue I have with the usage of {{ action_value }}

light_brightness:
  sequence:
    - service: light.turn_on
      data_template:
        entity_id: "{{ entity_id }}"
        #action_value: "{{ action_value }}"
        brightness: >-
          {# original #}
          {# % set current = states.light.wohnzimmer_stehlampe.attributes.brightness|default(0)|int % #}
          {# next has valid syntax and it works #}
          {% set current = state_attr("light.wohnzimmer_stehlampe", "brightness")|default(0)|int %}
          {# next has valid syntax and it works #}
          {% set entity = "light.wohnzimmer_stehlampe" %}
          {% set current = state_attr(entity, 'brightness')|default(0)|int %}
          {# ################################################## #}
          {# action_value is delivered when the script is called #}
          {#Syntax OK, but it doesn't work#}
          {% set action_value = "{{ action_value }}" %}
          {% set step = action_value | int * 255 / 360 %}
          {#% set step = 90 * 255 / 360 %#}
          {% set next = (current + step) | int %}
          {% if next > 255 %}
              {% set next = 255 %}
              {% if next < 10 %}
                  {% set next = 10 %}
                  {#turn_off, but how?, it looks like this is wrong, maybe in a separate step#}
                  {#% light.wohnzimmer_stehlampe.turn_off %#}
              {% endif %}
          {% endif %}
          {{ next }}
    # #the following is wrong:
    # #ERROR (MainThread) [homeassistant.helpers.service] Template rendered invalid service:
    # - service_template: >
        # {% if states.light.wohnzimmer_stehlampe.attributes.brightness|default(0)|int < 10 %}
          # light.turn_off
        # {% endif %}
      # data_template:
        # entity_id: "{{ entity_id }}"
        
light_brightness_test_up:
  sequence:
  - service: script.light_brightness
    data:
      entity_id: light.wohnzimmer_stehlampe
      action_value: 90

light_brightness_test_down:
  sequence:
  - service: script.light_brightness
    data:
      entity_id: light.wohnzimmer_stehlampe
      action_value: -60

use it just like a variable as if it were specified with ‘set’.

change this:

{# % set current = states.{{ entity_id }}.attributes.color_temp|default(0)|int % #}

to

{% set current = state_attr(entity_id, 'color_temp') | int %}

or if you want to pull it out of the state machine directly

{% set domain, name = entity_id.split('.') %}
{% set current = states[domain][name].attributes.color_temp | int %}

so your end code would be:

light_color_temp:
  sequence:
    - service: light.turn_on
      data_template:
        entity_id: "{{ entity_id }}"
        color_temp: >-
          {% set current = state_attr(entity_id, 'color_temp') | int %}
          {% set max = states('input_number.color_temp_max') | int %}
          {% set min = states('input_number.color_temp_min') | int %}
          {% set step = states('input_number.color_temp_step') | int %}
          {% if current >= max %}
            {{ min }} 
          {% elif current + step >= max %}
            {{ max }}
          {% else %}
            {{ current + step %}
          {% endif %}

On the second automation, use state_attr again but with brightness.

{% set current = state_attr(entity_id, 'brightness') | int %}

FYI, the default(0) isn’t needed. If the state returns a string or bad number, int will default to zero.

1 Like

Thank you very much!
This looks so easy if you know what to do :slight_smile:

I have still an issue with the action_value, I think this is still wrong:

          {% set step = action_value | int * 255 / 360 | int %}

light_brightness:
  sequence:
    - service: light.turn_on
      data_template:
        entity_id: "{{ entity_id }}"
        brightness: >-
          {% set max = 255 %}
          {% set min = 10 %}
          {% set step = action_value | int * 255 / 360 | int %}
          {% set current = state_attr(entity_id, 'brightness') | int %}
          {% if current + step >= max %}
            {{ max }} 
          {% elif current + step <= min %}
            {{ min }} 
          {% endif %}
    # #the following is wrong:
    # #ERROR (MainThread) [homeassistant.helpers.service] Template rendered invalid service:
    # - service_template: >
        # {% if state_attr(entity_id, 'brightness') < 10 %}
          # light.turn_off
        # {% endif %}
      # data_template:
        # entity_id: "{{ entity_id }}"
        
light_brightness_test_up:
  sequence:
  - service: script.light_brightness
    data:
      entity_id: light.dachgeschoss_decke
      action_value: 90

light_brightness_test_down:
  sequence:
  - service: script.light_brightness
    data:
      entity_id: light.dachgeschoss_decke
      action_value: -60

And maybe you have an idea how to turn_of the light if I am at the minimum or below?

It’s because you don’t have a service if the brightness is less than 10. Service_templates always need a service from every path of an if statement. Also, your previous section appears to be missing the else statement with {{ current + step }}.

I found a work around to call the light.turn_off based on a condition, but I am not yet happy with this “dirty” solution. It would be better if I could call the turn_off from inside the first part (where I assign the brightness) or if I could set a clear variable with clear 0 or 1 to be used in the second part istead of manipulating the brightness to use this value in the next step.

light_brightness:
  sequence:
    - service: light.turn_on
      data_template:
        entity_id: "{{ entity_id }}"
        brightness: >-
          {% set max = 255 %}
          {% set min = 23 %}
          {% set step = action_value | int * (max - min) / 360 | int %}
          {% set current = state_attr(entity_id, 'brightness') | int %}
          {% if current + step >= max %}
            {{ max }} 
          {% elif (current <= min) and (step < 0) %}
            {#use min - x as marker that light should turn_off #}
            {#but this is only a work around #}
            {#better would be to turn_off directly or to set a concret value like 0 or 1 #}
            {#the real assigned brightness is not exactly what will be set in the script #}
            {#if the assigned value should be 20 the real value could be 18 #}
            {#it looks like there are some possible discrete values for the brightness: ..., 18, 23, ... #}
            {{ min - 5}} 
          {% elif current + step <= min %}
            {{ min }} 
          {% else %}
            {{ current + step | int }}
          {% endif %}
    - service_template: >
          {% set min = 23 %}
          {% set current = state_attr(entity_id, 'brightness') | int %}
          {% if current < min - 3 %}
            light.turn_off
          {% endif %}
      data_template:
        entity_id: "{{ entity_id }}"

Hi, I have a similar script to dim the lights but somehow I keep getting the following errors:

expected int for dictionary value @ data[‘brightness’]

Here is my script:

  dimmer_swipe_up:
    sequence:
      - service: light.turn_on
        data_template:
          entity_id: "{{ light_id }}"
          brightness: >-
            {% set current = state_attr(light_id, 'brightness') | int %}
            {% set step = states('input_number.light_step')|int %}
            {% set next = current + step %}
            {% if next > states('input_number.light_maximum')|int %}
              {% set next = states('input_number.light_maximum')|int %}
            {% endif %}
            {{ next }}

I pass the variable “light_id” from a python script, script name is “script.dimmer_swipe_up”:

http://192.xxx.xxx.xxx:8123/api/services/script/turn_on
script.dimmer_swipe_up
{‘entity_id’: ‘script.dimmer_swipe_up’, ‘variables’: {‘light_id’: ‘light.dining_lights’}}
<Response [200]>

The light was on when the script was called. So that was not the problem.
To test if the variable “light_id” was passed successfully, I used the following code to verify it:

{% set current = states.light.dining_lights.attributes.brightness|default(0)|int %}

The dimming works with that code without any error messages if I hard-coded the entity_id. I must be missing something, can any of you please help me? I am quite confused now why it doesn’t work if I used a variable. Thank you in advance!

try calling the script with this service:

  service: script.dimmer_swipe_up
  data:
    light_id: light.dining_lights

Hi Petro,

Thank you for your feedback. So I changed the api call to call the script directly:

http://192.xxx.xxx.xxx:8123/api/services/script/dimmer_swipe_up
dimmer_swipe_up
{‘data’: {‘light_id’: ‘light.dining_lights’}}
<Response [200]>

And I got the following error:

2019-01-04 21:23:37 ERROR (MainThread) [homeassistant.helpers.service] Error rendering data template: UndefinedError: ‘light_id’ is undefined

It seems that the variable “light_id” is not passing to the script now. I did not change anything in the script.

it wouldn’t be the script, it would be the automation that passes the variable.

I do not have an automation for the dimmer. Only the python script and use API to call the script directly. There is no automation involved. The reason for this is because the event I try to detect is not a normal HA event but comes from a z-wave device that sends multilevel command. (not scene). To be able to detect that (swipe up or swipe down) I wrote a simple python script to monitor OZW_Log.txt for that particular command, get the values, compare them and generate the necessary actions based on that. Then use that information to call the script for dimming.

I don’t have z-wave lights (LIFX). Otherwise I can associate z-wave lights directly to the dimmer function of the light remote. I don’t need HA in that configuration.

Automation or not, the script should work. It is basically the same code you recommended. I think I am just missing something that is not obvious.

then how are you passing the variable to the script? Can you post the python?

Also, if you are using python, why not just get rid of the script? All this work can be done in the python script.

entity_id = 'light.dining_lights'
current = None
step = None

e = hass.states.get(entity_id)
if e and 'brightness' in e.attributes:
    current = e.get('brightness')

s = hass.states.get('input_number.light_step')
if s:
    step = int(s.get('state'))

m = hass.states.get('input_number.light_maximum')
if m:
    max_level = int(s.get('state'))

if current and step and max_level:
    next = current + step
    if next > max_level:
        next = max_level
    
    hass.services.call('light','turn_on', {'entity_id': entity_id,'brightness':next}, False)

Wow Petro, I am speechless that you cooked up the python script for me! :wink: Tbh, I am not familiar with python. I just “copied” some codes from this forum and changed it to suit my needs.

The basic functionalities are there in your script but I need the entity_id to be dynamic: I have 5 group of lights and 2 dimmers. Each dimmer has 4 buttons (Aeotec quad wallmote) that dims up/down each of its assigned entities. 1st dimmer: light.living_room, light.dining_lights, light.corner_light, light.stairs_light and 2nd dimmer: light.bedroom. Button 1 - 4 is assigned to the first 4 pairs and the 1st button in the 2nd dimmer is assigned to light.bedroom.

Here is the python code to detect the swipe up/down actions:

#!/usr/bin/env python3
import socket
import requests
from subprocess import Popen, PIPE
import json

# Match Rule
# Match the start of swipe
MATCHER1 = 'Received Configuration report: Parameter=9'
# Match the end of swipe
MATCHER2 = 'Received Configuration report: Parameter=10'
# Match which button is used on the wallmote quad
MATCHER3 = 'Decrypted Packet: 0x00, 0x70, 0x06, 0x09, 0x04'

# HA information
TOKEN = 'secret'
#TOKEN = False

OZW_LOG = '/home/homeassistant/.homeassistant/OZW_Log.txt'

debug = True

# Open the tail of our OZW_Log and scan for new lines;
log = Popen(('/usr/bin/tail', '-F', '-n', '0', OZW_LOG), stdout=PIPE)
while True:

    # Get most recent line and massage it;
    line = log.stdout.readline()
    if not line:
      break
    line = line.strip().decode('utf-8')


    # Fast match
    if MATCHER1 not in line:
      if MATCHER2 not in line:
       if MATCHER3 not in line:
        continue

    if "Parameter=9" in line:
      parameter = line.split(',')[-2].replace('Received Configuration report: ','').strip()
      parameter9 = parameter.split('=')[1].strip()
      nodeID = line.split(',')[1].strip().lower()
      if "node002" in nodeID:
        node = "light.living_room"
      if "node003" in nodeID:
        node = "light.bedroom"

      value9 = line.split('=')[-1].strip()
      value9 = int(value9)
      continue
    elif "Parameter=10" in line:
       parameter = line.split(',')[-2].replace('Received Configuration report: ','').strip()
       parameter10 = parameter.split('=')[1].strip()
       #parameter10 = parameter
       value10 = line.split('=')[-1].strip()
       value10 = int(value10)
    elif "Decrypted Packet:" in line:
       button = line.split(',')[-4].replace(' 0x0','').strip()
       continue

    if parameter9 and parameter10 is not None:
      if value9 < value10:
        action = 'swipe_up'
      else:
        action = 'swipe_down'

    if node is not None:
      if "light.living_room" in node:
        if "1" in button:
          light_id = "light.living_room_light"
        if "2" in button:
          light_id = "light.dining_lights"
        if "3" in button:
          light_id = "light.corner_light"
        if "4" in button:
          light_id = "light.stairs_light"
        elif "light.bedroom" in node:
          if "1" in button:
           light_id = 'light.bedroom_light'
    
    event = 'dimmer_{0}'.format(action)
    URI = URI = 'http://192.xxx.xxx.xxx:8123/api/services/script/{0}'.format(event)
    if debug:
        print(URI)
    if debug:
        print(event)

    if event:
        #data = {"entity_id": "{0}".format(event), "variables":{"light_id":"{0}".format(light_id)}}
        data = {"data":{"light_id":"{0}".format(light_id)}}
        if debug:
            print(data)

        if TOKEN:
            resp = requests.post(URI, data=json.dumps(data), headers={'Authorization': 'Bearer {0}'.format(TOKEN), 'content-type': 'application/json'})
        else:
            resp = requests.post(URI, data=json.dumps(data), headers={'content-type': 'application/json'})

        if debug:
            print(resp)

The codes are redundant I know. Your solution is much more elegant but since I am noob in python, I will need some time to understand it to extend your code to use in my situation. Perhaps this is a good starting point for me:

https://www.home-assistant.io/cookbook/python_component_simple_alarm/

I supposed I can use a python script as custom_component?

I really appreciate your help in cooking up the python script!! :wink:

I’m confused. How does that script get information into home assistant? Nevermind, it’s using web calls. When and how does that code get executed or is it running at all times? Do you call that via curl? Well anyways, I think your problem lies with the unconventional way that you are getting the information into home assistant. I’m not sure that passes through the same code when executing a script. That may be your problem.

Also, just a tid bit of information:

data = {"data":{"light_id":"{0}".format(light_id)}}

format is not needed here. light_id is already a string. You can just place light_id in the dictionary.

data = {"data":{"light_id":light_id}}

that won’t fix your problem though. I’d start by verifying that your data is actually getting sent to the script.

Yeah I am using web calls and run the script continuously in a screen session on my Pi. I have to use the unconventional way because the multilevel command does not get through to HA (as far as I know this is not a standard central scene).

With my original code (calling script.turn_on) I got the following errors. I supposed the variable “light_id” got passed successfully (bold text):

2019-01-05 21:12:27 ERROR (MainThread) [homeassistant.core] Error executing service <ServiceCall script.dimmer_swipe_up (c:eb166d4ca09046c297d702f2beebb36f): light_id=light.dining_lights>
Traceback (most recent call last):
File “/home/homeassistant/.pyenv/versions/3.6.3/envs/homeassistant-3.6.3/lib/python3.6/site-packages/homeassistant/core.py”, line 1130, in _safe_execute
await self._execute_service(handler, service_call)
File “/home/homeassistant/.pyenv/versions/3.6.3/envs/homeassistant-3.6.3/lib/python3.6/site-packages/homeassistant/core.py”, line 1143, in _execute_service
await handler.func(service_call)
File “/home/homeassistant/.pyenv/versions/3.6.3/envs/homeassistant-3.6.3/lib/python3.6/site-packages/homeassistant/components/script.py”, line 123, in service_handler
context=service.context)
File “/home/homeassistant/.pyenv/versions/3.6.3/envs/homeassistant-3.6.3/lib/python3.6/site-packages/homeassistant/components/script.py”, line 181, in async_turn_on
kwargs.get(ATTR_VARIABLES), context)
File “/home/homeassistant/.pyenv/versions/3.6.3/envs/homeassistant-3.6.3/lib/python3.6/site-packages/homeassistant/helpers/script.py”, line 130, in async_run
await self._handle_action(action, variables, context)
File “/home/homeassistant/.pyenv/versions/3.6.3/envs/homeassistant-3.6.3/lib/python3.6/site-packages/homeassistant/helpers/script.py”, line 172, in _handle_action
action, variables, context)
File “/home/homeassistant/.pyenv/versions/3.6.3/envs/homeassistant-3.6.3/lib/python3.6/site-packages/homeassistant/helpers/script.py”, line 261, in _async_call_service
context=context
File “/home/homeassistant/.pyenv/versions/3.6.3/envs/homeassistant-3.6.3/lib/python3.6/site-packages/homeassistant/helpers/service.py”, line 81, in async_call_from_config
domain, service_name, service_data, blocking=blocking, context=context)
File “/home/homeassistant/.pyenv/versions/3.6.3/envs/homeassistant-3.6.3/lib/python3.6/site-packages/homeassistant/core.py”, line 1101, in async_call
processed_data = handler.schema(service_data)
File “/home/homeassistant/.pyenv/versions/3.6.3/envs/homeassistant-3.6.3/lib/python3.6/site-packages/voluptuous/schema_builder.py”, line 267, in call
return self._compiled(, data)
File “/home/homeassistant/.pyenv/versions/3.6.3/envs/homeassistant-3.6.3/lib/python3.6/site-packages/voluptuous/schema_builder.py”, line 589, in validate_dict
return base_validate(path, iteritems(data), out)
File “/home/homeassistant/.pyenv/versions/3.6.3/envs/homeassistant-3.6.3/lib/python3.6/site-packages/voluptuous/schema_builder.py”, line 427, in validate_mapping
raise er.MultipleInvalid(errors)
voluptuous.error.MultipleInvalid: expected int for dictionary value @ data[‘brightness’]

So the variable ‘light_id’ was passed successfully but somehow the brightness value is not correct (exepected int for dictionary value @ data[‘brightness’].

I have a feeling that I am almost there…

Do u know how to pass variable in a web call to HA script?