Has anyone succeeded in working with response_variables?

To illustrate Petro’s point…

Using if/then in the Jinja template will work:

alias: Add Three or Five Jinja
sequence:
  - variables:
      if_then_add: |
        {% set x = value + 5 if value is odd else value + 3 %}
        {{ {'value': x} }}
  - stop: Addition complete
    response_variable: if_then_add
mode: single
fields:
  value:
    name: Value
    required: true
Using If/Then Actions does not work

Neither of the following will work.

alias: Add Three or Five If/Then Inside
sequence:
  - if:
      - condition: template
        value_template: "{{ value is odd }}"
    then:
      - variables:
          value_plus_three_or_five: |
            {{ {'value': value + 5} }}
      - stop: Added Five
        response_variable: value_plus_three_or_five
    else:
      - variables:
          value_plus_three_or_five: |
            {{ {'value': value + 3} }}
      - stop: Added Three
        response_variable: value_plus_three_or_five
mode: single
alias: Add Three or Five If/Then Outside
sequence:
  - if:
      - condition: template
        value_template: "{{ value is odd }}"
    then:
      - variables:
          value_plus_three_or_five: |
            {{ {'value': value + 5} }}
    else:
      - variables:
          value_plus_three_or_five: |
            {{ {'value': value + 3} }}
  - stop: Added to Value
    response_variable: value_plus_three_or_five
mode: single

well, that SHOULD work if you put the stop inside the if. It won’t work if the stop is after the if. So I just avoid dealing with it.

I thought so too, but I tested and got no response with the stop inside the if…

1 Like

ok, thank you for clearing this out! I guess I’ll take the easy route and use a helper input boolean instead of a script return variable that I can control during the execution of the script.

This basically means that the whole script visual editor is useless for scripts that return a value.

When programming functions it’s common to either have a bunch of returns or to define a variable, set it as you go along, and then return it at the end. Neither of these are possible using the HA visual script editor.

At the very least we need a way to call stop from within actions (preferably with a value rather than having to define a variable first) but ideally a way to modify sequence-level variables. Since the outer variables are available to read surely it must be close to possible to modify them, even if it’s just a property of a dict.

1 Like

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.