Has anyone succeeded in working with response_variables?

I’ve also had issues based on the script mode btw.
For me using queued mode breaks the response variable…

alias: lock1
sequence:
  - variables:
      test:
        value: true
  - stop: Test
    response_variable: test
mode: queued

This script will not return a value in queued mode; tested from the dev tools call service UI. This could be a bug…

3 Likes

I can replicate this issue. Response is returned for single, restart and parallel, but not for queued.

Edit: It has been reported in this GitHub issue.

1 Like

I agree with this sentiment. I started converting to a more DRY automation base leveraging scripts and response variables. It’s counter intuitive for programmers that you can’t define a variable at top of scope and alter it somewhere nested scope and have that value bubble up the scope.

The result of this design is that you have a TON of repeated code everywhere, which diminishes the value of scripts.

2 Likes

Can someone explain why the response_variable is empty if I’m using a choose-sequence oder similar?

Can’t be a scope-issue… the variable “limit” is filled in the notify, but response_variable returns nothing…

alias: test
sequence:
  - variables:
      limit: |
        {{ {'value': 1200 } }}
  - choose:
      - conditions: []
        sequence:
          - service: notify.notify
            metadata: {}
            data:
              message: "{{ limit.value }}"
          - stop: Done
            response_variable: limit
mode: single

Can someone explain and give me a hint?
Thanks.

variable scope in automations does not get passed inside if statements, chooses, repeats, or any other nested actions.

with your simple script, this would work

alias: test
sequence:
  - variables:
      limit: |
        {{ {'value': 1200 } }}
  - choose:
      - conditions: []
        sequence:
          - service: notify.notify
            metadata: {}
            data:
              message: "{{ limit.value }}"
  - stop: Done
    response_variable: limit
mode: single

Thank you for your reply.
Yes, your script works but the choose-action will be pretty useless that way?!

My original script is longer and more complex with 3 choose options, this little script was shortened to show what I want.

To be honest, I don’t get it.
Why is the notification filled correctly with the value of the variable when the variable does not get passed inside nested actions?

How can I build an if/else/elsif in a script and return the result to a automation?

You have to build the if, then, else logic in the variables template itself. You can’t build/modify the variables in nested actions because it’s not possible.

alias: test
sequence:
  - variables:
      limit: >
        {% if ... %}
          {{ {'value': 1200 } }}
        {% else %}
          {{ ... }}
        {% endif }}
  - choose:
      - conditions: []
        sequence:
          - service: notify.notify
            metadata: {}
            data:
              message: "{{ limit.value }}"
  - stop: Done
    response_variable: limit
mode: single

Thank you for your help.
I still do not understand why that’s not possible, but that’s not the topic.

I’m pretty sure the responded variable isn’t usable in automations with nested actions too.
(I’ve tried and it doesn’t worked for me) I’m setting the value into an helper entity.
This I can use in nested-actions.

Just if anyone is trying the same thing:

service: input_number.set_value
data:
  value: "{{ limit.value |float }}"
target:
  entity_id: input_number.limit

That is the literal reason. If you don’t understand that statement, I suggest you read up on what scope means in the context of coding.

In a programming language, scope refers to the area where a function or variable is visible and accessible to other code . Below are some common terms associated with scope: Global scope refers to a global or public space. Local scope refers to a local or restricted region.

Each nested level has it’s own local scope, and it’s not shared between parent levels.

FYI the float is unnecessary here.

Hi, all!! can’t get working response_variable using in python script pro integration. so, from trace i see the variable is evaluated, but when i am using services in dev tool, by running this script i do not see response value, although it is defined in script UI. any help from any one? do i misunderstanding response_variable concept?

my script trace:

this:
  entity_id: script.1707049843680
  state: 'off'
  attributes:
    last_triggered: '2024-02-04T15:04:37.147150+00:00'
    mode: single
    current: 0
    friendly_name: Python test
  last_changed: '2024-02-04T15:04:37.560387+00:00'
  last_updated: '2024-02-04T15:04:37.560387+00:00'
  context:
    id: 01HNTACR0FF2HG9TZTQK7DFD2G
    parent_id: null
    user_id: 65d3de50a8c140c5850a620b71ad980c
context:
  id: 01HNTAHAX6V3D5QVNQAGE7EJKS
  parent_id: null
  user_id: 65d3de50a8c140c5850a620b71ad980c
value_testing:
  value: 43195

my script:

alias: Python test
sequence:
  - service: python_script.exec
    data:
      file: /config/custom_components/pyscript_old/get_equal_time_frames.py
      cache: false
      start_time: "08:00:05"
      min_period: "300"
      end_time: "20:00:00"
    response_variable: value_testing
fields: {}

my python script:

from datetime import datetime

logger.info(data)

# start time
#start_time = data['start_time']
#end_time = data['end_time']
#min_period = data['min_period']

# convert time string to datetime
#t1 = datetime.strptime(start_time, "%H:%M:%S")
t1 = datetime.strptime(data['start_time'], "%H:%M:%S")

logger.info('Start time:', t1.time())

#t2 = datetime.strptime(end_time, "%H:%M:%S")
t2 = datetime.strptime(data['end_time'], "%H:%M:%S")
logger.info('End time:', t2.time())

# get difference
delta = t2 - t1
logger.info(f"Time difference is {delta.total_seconds()} seconds")

value = delta.total_seconds()

call on service page:

In practice it doesn’t work. Passing response variables in the specified scope is still blank on the other side. So either the docs are wrong or there is a bug. In addition, what the community has been saying is this is simply not a workable paradigm, even if it did work as documented. Variables init’d outside of a branching scope should be lifted to the outer scope when set.

No, this 100% works if you keep it within the proper scope. I’m using it. Whatever you’re doing is not correct. Here’s a full example:

  - variables:
      calendar: calendar.xxx
  - service: calendar.get_events
    data:
      start_date_time: "{{ today_at() }}"
      end_date_time: "{{ today_at().replace(year=now().year + 2, day=1, month=1) }}"
    target:
      entity_id: "{{ calendar }}"
    response_variable: raw_events
  - variables:
      gathered: >
        {{ {'events': raw_events[calendar].events} }}

So please post your code. I’ll help you fix it.

EDIT: If you’re referring to this post:

If statements are a nested items. It won’t work with if statements, hence why you have to do…

So if the stop, which passes the response variable, is inside the conditional and the variable is populated inside of the same conditional it doesn’t pass the value. You’re saying this is by design?

Variables do not pass from inside a nested item to the outer layer.

e.g. this does not work.

- if:
  ...
  then:
  - variables:
       abc: 1
  else:
  ...
- variables:
    xyz: "{{ abc }}"

Yes, this is by design. That doesn’t mean it can’t change, just wasn’t implemented when variables were first introduced.

Thanks for clearing that up for me.

I have a similar question and I just can’t find such an example.
I have a script to call a service, just starting to program all things after.
Here is the script:

alias: Check and Add Spotify Favorite
sequence:
  - action: spotifyplus.check_track_favorites
    metadata: {}
    data:
      entity_id: media_player.spotifyplus_kevin_brown
    response_variable: favorite_exists
  - variables:
      test: |
        {{ favorite_exists }}
description: Checks and adds a favorite if the song is not already in favorites
icon: mdi:account-plus

The response in the service is like this (some info redacted):

this:
  entity_id: script.check_and_add_spotify_favorite
  state: 'off'
  attributes:
    last_triggered: '2024-10-22T22:07:03.487018+00:00'
    mode: single
    current: 0
    icon: mdi:account-plus
    friendly_name: Check and Add Spotify Favorite
  last_changed: '2024-10-22T22:07:03.769852+00:00'
  last_reported: '2024-10-22T22:07:03.769852+00:00'
  last_updated: '2024-10-22T22:07:03.769852+00:00'
  context:
    id: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    parent_id: null
    user_id: xxxxxxxxxxxxxxxxxxxxxx
context:
  id: xxxxxxxxxxxxxxxxxxxxx
  parent_id: null
  user_id: xxxxxxxxxxxxxxxxxxx
favorite_exists:
  user_profile:
    country: US
    display_name: Kevin Brown
    email: xxxxxxxxxxxxxxxxxxxxxxxxxx
    id: xxxxxxxxxxxxxxxxxxxx
    product: premium
    type: user
    uri: spotify:user:xxxxxxxxxxxxxxxxxxxxxxx
  result:
    2tUBqZG2AbRi7Q0BIrVrEj: false

The only thing I want is the “false” or “true” at the end. This literally means the current song is my favorites “true” or not “false”.

I cannot even seem to get the value. If I look at the variable, it is populated:

test:
  user_profile:
    country: US
    display_name: Kevin Brown
    email: xxxxxxxxxxxxxxxxxxx
    id: xxxxxxxxxxxxxxxxxxxxxxxx
    product: premium
    type: user
    uri: spotify:user:xxxxxxxxxxxxxxxxxxx
  result:
    2tUBqZG2AbRi7Q0BIrVrEj: false

The key “2tUBqZG2AbRi7Q0BIrVrEj” changes with each request.
I can get deeper with this:

variables:
  test: |
    {{ favorite_exists.result }}

Which yields:

test:
  50nfwKoDiSYg8zOCREWAm5: false

How do I get this value under result, what is the syntax? Simply put, in the following I would like to edit that template for test so that it returns “true” or “false” for further processing in the script to update GUI in certain ways.

- variables:
      test: |
        {{ favorite_exists.result**what_belongs_here** }}

Note: I suppose I can just create a new sensor that updates with each song change and just use that, but I was hoping not to do that

Solution is:

variables:
  test: "{{ (favorite_exists.result.values() | list)[0] }}"

Just a minor improvement- there’s no need to create a list and then pull out the first element. Just grab the first element directly:

variables:
  test: "{{ favorite_exists.result.values() | first }}"
2 Likes

Are you just trying to add the currently playing Spotify track to your track favorites via the spotifyplus service? If so, just call the save_track_favorites service:

action: spotifyplus.save_track_favorites
data:
  entity_id: media_player.spotifyplus_kevin_brown

It does not raise an error if the track is already a favorite.

Regarding your original question:

Copy / paste this into the Developer Tools \ Template tool:

{% set test = {
  "user_profile": 
  {
    "country": "US",
    "display_name": "Kevin Brown",
    "email": "xxxxxxxxxxxxxxxxxxx",
    "id": "xxxxxxxxxxxxxxxxxxxxxxxx",
    "product": "premium",
    "type": "user",
    "uri": "spotify:user:xxxxxxxxxxxxxxxxxxx"
  },
  "result":
  {
    "2tUBqZG2AbRi7Q0BIrVrEj": false
  }
}%}

User Profile Info:
  Display Name = {{ test.user_profile.display_name }}
  Country = {{ test.user_profile.country }}
  Product = {{ test.user_profile.product }}

Result Info:
  Spotify URI = {{ test.result.keys() | first }}
  Is Favorite = {{ test.result.values() | first }}

Will show these results:

User Profile Info:
  Display Name = Kevin Brown
  Country = US
  Product = premium

Result Info:
  Spotify URI = 2tUBqZG2AbRi7Q0BIrVrEj
  Is Favorite = False

Hope it helps!

Yes @thlucas … i was worried that the track would be added twice. Are you saying no need for a test to see if it is already there?

@kbrown01
correct - it it’s already a favorite then it does nothing; if not, it adds it.

The only limitation is that you have to call the appropriate service depending on the playing context:

  • for a track, call spotifyplus.save_track_favorites
  • for an artist (of the playing track), call spotifyplus.follow_artists
  • for an album (of the playing track), call spotifyplus.save_album_favorites
  • for a podcast episode, call spotifyplus.save_episode_favorites
  • for a podcast (of the playing episode), call save_show_favorites
  • for an audiobook, call spotifyplus.save_audiobook_favorites
  • for a playlist (that owns the playing track), call spotifyplus.follow_playlist