Substitution results in "extra keys not allowed @ data" in frontend dictionary

I am wondering why the following substitutions are failing. Context. I have a script which is handling some data and calling an action (service). Evohome.set_zone_override. If I call the script from dev_tools it works fine. The part which is not working is the frontend substitution of the values from the input selectors.

So here is the backend script - as I results in the expected target room temperature change.

alias: heating_override
description: Script to override the default room set point
fields:
  target_temperature:
    name: target_temperature
    description: The desired room temperature
    required: true
    selector:
      number:
        min: 5
        max: 25
        step: 0.5
        unit_of_measurement: °C
  target_room:
    name: target_room
    description: The desired room
    required: true
    selector:
      entity:
        domain: climate
  target_duration:
    name: target_duration
    description: The desired time period
    required: false
    selector:
      number:
        min: 10
        max: 1440
        step: 10
        unit_of_measurement: minutes
sequence:
  - action: evohome.set_zone_override
    data:
      setpoint: "{{ target_temperature }}"
      entity_id: "{{ target_room }}"
      duration:
        minutes: "{{ target_duration }}"
mode: single

Ok, so then hard coding the front end POC code [like] below results in a repeat of the action generated from the dev_tools action. All good.

type: custom:mushroom-chips-card
chips:
  - type: entity
    entity: sun.sun
    tap_action:
      action: fire-dom-event
      browser_mod:
        service: browser_mod.popup
        target: {}
        data:
          user_id: THIS
          size: classic
          title: Heating Override?
          dismissable: false
          timeout: 60000
          content:
            - name: p_minutes
              label: >-
                Duration (max 240 [4h]) - leave this blank to override until
                next programmed set point.
              selector:
                number:
                  max: 240
                  min: 10
                  step: 20
            - name: p_entity
              label: Room Selector
              selector:
                entity:
                  include_entities:
                    - climate.test_room
                    - climate.test_room1
                    - climate.test_room2
                  filter:
                    domain: climate
            - name: p_setting
              label: Target Temperature
              selector:
                number:
                  min: 16
                  max: 21
                  mode: slider
          right_button: Ok
          right_button_action:
            - target:
                entity_id: script.heating_override
              action: script.turn_on
              data:
                variables:
                  target_room: climate.test_room
                  target_temperature: 16
                  target_duration: 10

This is what breaks it:

                  target_room: "{{p_entity}}"
                  target_temperature: "{{p_setting}}"
                  target_duration: "{{p_minutes}}"

I am inputting in the selector UI respectively:

climate.test_room
17
10

This is the error:

Failed to perform the action script/turn_on. extra keys not allowed @ data['p_minutes']

It feels like I am miss-coding how the variables are substituted from the input selectors into the script data call variables. I have tried the following two variations, both without success.

                  target_room: '[p_entity]]'
                  target_temperature: '[[p_setting]]'
                  target_duration: '[[p_minutes]]'
                  target_room: p_entity
                  target_temperature: p_setting
                  target_duration: p_minutes

I am hoping someone has some insight or experience to offer on this.

Isn’t there a square bracket missing?

Browser Mod will pass the Popup form fields directly without extra config. So you only need. action: evohome.set_zone_override and nothing else.

Then in your script, you can can either use those directly, or use a variable: step to template the inputs into anothe variable. Alternatively you can name the form fields in your Browser Mod Popup to match the script fields.

A tip as well when working with scripts is you can see what is passed via Traces and looking at the changed variables for the first/trigger step.

Kerching, well sort of. The trace shows the variables are passed exactly as expected. So the fault must be in the script. Which what you said, even though I only understand half of the answer.


variables:
  target_room: '{{p_entity}}'
  target_temperature: '{{p_setting}}'
  target_duration: '{{p_minutes}}'
p_entity: climate.test_room
p_setting: 16
p_minutes: 10

Unfortunately that deepens the confusion as to what is a parameter name and what is a variable in the script code I wrote. Also why it works from dev tools and why hard coded values also work??? In the same script. This is a total mystery to me.
The error in the trace shows that the script fails because target_temperature is undefined. Isn’t that what the fields: do? Define named variables so that incoming variables can be assigned and the substituted in the service call later?


fields:
  target_temperature:
    name: target_temperature
    description: The desired room temperature
    required: true
    selector:
      number:
        min: 5
        max: 25
        step: 0.5
        unit_of_measurement: °C

Really confusing. Sorry about this.

FYI.
Just assume I am frantically watching YouTube videos, using ChatGPT and reading loads of posts which are out of date or help which doesn’t quite match my case and that’s where I am. So I don’t know, what I am doing, yet. It is like trying to learn to speak English from a dictionary, no idea how to assemble stuff.

Can you share your script yaml?

It’s at the top of the post

Got it. What you are seeing is that you are actually passing an extra variables: dictionary. That may have arisen from my brief suggestion as to using variables: which is in relation to the script itself.

Your call from popup action only needs to be that below.

          right_button_action:
            - target:
                entity_id: script.heating_override
              action: script.turn_on

The rest of the magic you do in your script. e.g. (not tested of course)

The variables script sequence sets the script field variables to alternate if not set (immediate if). Since the field will be undefined from your Browser Mod call (which is OK, fields are for visual guide, blueprints etc.) we can test that. The |default(None) is to save warnings in logs, so if the field is not defined, give it a value None which is falsy so iif will take the false value, being your alternate passed in value.

NOTE: I copied yaml after using Script editor to get this as accurate as I could. HA save script process decided to break the first variable over two lines (so >) but not the other two (so |). These could be single quoted lines but I always have the habit of using multiline templates for variables.

alias: heating_override
description: Script to override the default room set point
fields:
  target_temperature:
    name: target_temperature
    description: The desired room temperature
    required: true
    selector:
      number:
        min: 5
        max: 25
        step: 0.5
        unit_of_measurement: °C
  target_room:
    name: target_room
    description: The desired room
    required: true
    selector:
      entity:
        domain: climate
  target_duration:
    name: target_duration
    description: The desired time period
    required: false
    selector:
      number:
        min: 10
        max: 1440
        step: 10
        unit_of_measurement: minutes
sequence:
  - variables:
      target_temperature: >
        {{ iif(target_temperature|default(None), target_temperature, p_setting)
        }}
      target_room: |
        {{ iif(target_room|default(None), target_temperature, p_room) }}
      target_duration: |
        {{ iif(target_room|default(None), target_temperature, p_minutes) }} 
  - action: evohome.set_zone_override
    data:
      setpoint: "{{ target_temperature }}"
      entity_id: "{{ target_room }}"
      duration:
        minutes: "{{ target_duration }}"
mode: single

While this makes scripts a bit lengthier, it does make your script versatile in that you can call directly to test, use descriptive variables, but also pass in different data and handle that as well.

And as you are lerning, a final point. The validation of fields is all for when the script is used in the UI. There is no validation when run. If you would want to validate then your script needs to do that.

Thank you so much again for your comprehensive help and answer. I wish I was not therefore saying, I can’t get it to work. I feel I must provide a comprehensive as possible account so any further time isn’t wasted.

Specifically the shortened call in the pop up does not result in the script being called.

So this:

          right_button_action:
            - target:
                entity_id: script.heating_override
              action: script.turn_on

Does not show “script was run x seconds ago” in the script repository UI. Nor does it appear in the logbook as having run.

If I add this back in it then fails with the extra dictionary error:

              data:
                variables:
                  target_room: "{{p_entity}}"
                  target_temperature: "{{p_setting}}"
                  target_duration: "{{p_minutes}}"

On a slightly different tack. I notice that the logbook does show a link to the script, which brings up a UI wrapper around the script itself.

So if I enter those values and hit “Run” , it works ok. I guess this is the same as calling it from dev_tools actions:
Untitled

And the room setting is changed for 1 min as shown in the evhome native phone app.
The script code is the original code with out the suggested validation as including that did not overcome the pervious issue, so for now I removed that chunk again. So this is the complete script code.

alias: heating_override
description: Script to override the default room set point
fields:
  target_temperature:
    name: target_temperature
    description: The desired room temperature
    required: true
    selector:
      number:
        min: 5
        max: 25
        step: 0.5
        unit_of_measurement: °C
  target_room:
    name: target_room
    description: The desired room
    required: true
    selector:
      entity:
        domain: climate
  target_duration:
    name: target_duration
    description: The desired time period
    required: false
    selector:
      number:
        min: 10
        max: 1440
        step: 10
        unit_of_measurement: minutes
sequence:
  - action: evohome_1.set_zone_override
    data:
      setpoint: "{{ target_temperature }}"
      entity_id: "{{ target_room }}"
      duration:
        minutes: "{{ target_duration }}"
mode: single

This code on the lovelace side is successful - but only if I DO NOT enter anything in the selectors and just hit the “Override” button. That will just pass the hard coded values.

type: custom:mushroom-chips-card
chips:
  - type: entity
    entity: sun.sun
    tap_action:
      action: fire-dom-event
      browser_mod:
        service: browser_mod.popup
        target: {}
        data:
          size: classic
          title: Override the heating?
          right_button: "Yes"
          left_button: "No"
          dismissable: true
          autoclose: false
          timeout: 5000
          left_button_action:
            action: browser_mod.close_popup
          right_button_action:
            action: browser_mod.popup
            data:
              dismissable: true
              right_button: Override
              left_button: Cancel
              left_button_action:
                action: browser_mod.close_popup
              user_id: THIS
              size: classic
              title: Heating Override?
              timeout: 60000
              content:
                - name: p_minutes
                  label: >-
                    Duration (max 240 [4h]) - leave this blank to override until
                    next programmed set point.
                  selector:
                    number:
                      max: 240
                      min: 10
                      step: 20
                - name: p_entity
                  label: Room Selector
                  selector:
                    entity:
                      include_entities:
                        - climate.drawing_room
                        - climate.scarlett_room
                        - climate.sid_bedroom
                        - climate.spare_room
                        - climate.dining_room
                        - climate.ant_office
                      filter:
                        domain: climate
                - name: p_setting
                  label: Target Temperature
                  selector:
                    number:
                      min: 16
                      max: 21
                      mode: slider
              right_button_action:
                action: script.turn_on
                target:
                  entity_id: script.heating_override
                data:
                  variables:
                    target_room: climate.ant_office
                    target_temperature: 16
                    target_duration: 1

As soon as I enter anything in the selectors and then hit override it gives the dictionary error and does not call the script sucessfully. No evidence of running in the traces or the logbook. This make me believe that the error is too early to be a script problem. Otherwise I would be seeing “errror” xyz variable is undefined or something similar in the script trace.

As an aside I completely understand the need for validation in the final script, but for now I am just learning and trying to get minimum viable solution.

err hang on.

So this code on the frontend worked without any problem completely.

type: custom:mushroom-chips-card
chips:
  - type: entity
    entity: sun.sun
    tap_action:
      action: fire-dom-event
      browser_mod:
        service: browser_mod.popup
        target: {}
        data:
          size: classic
          title: Override the heating?
          right_button: "Yes"
          left_button: "No"
          dismissable: true
          autoclose: false
          timeout: 5000
          left_button_action:
            action: browser_mod.close_popup
          right_button_action:
            action: browser_mod.popup
            data:
              dismissable: true
              right_button: Override
              left_button: Cancel
              left_button_action:
                action: browser_mod.close_popup
              user_id: THIS
              size: classic
              title: Heating Override?
              timeout: 60000
              right_button_action:
                action: script.turn_on
                target:
                  entity_id: script.heating_override
                data:
                  variables:
                    target_room: climate.ant_office
                    target_temperature: 16
                    target_duration: 1

Untitled
corresponding result in the evohome app for my office.

But of course, it is hard coded.
This makes me sure this is a selectors problem in the data section of the popup and not the script?
What am I missing?

Missed one thing before. script.turn_on is to enable the script. Your action should just be script.heating_override

ok - let me work that back in

Another tip here is to view YAML mode in Developer Tools to see what it has translated from UI input. You would see it would be showing action: script.heating_override

ok - now it IS calling the script - I am getting data type conversion errors which is expected as the lovelace code it just passing strings to the script I think. But this is code that DID call the script

type: custom:mushroom-chips-card
chips:
  - type: entity
    entity: sun.sun
    tap_action:
      action: fire-dom-event
      browser_mod:
        service: browser_mod.popup
        target: {}
        data:
          size: classic
          title: Override the heating?
          right_button: "Yes"
          left_button: "No"
          dismissable: true
          autoclose: false
          timeout: 5000
          left_button_action:
            action: browser_mod.close_popup
          right_button_action:
            action: browser_mod.popup
            data:
              dismissable: true
              right_button: Override
              left_button: Cancel
              left_button_action:
                action: browser_mod.close_popup
              user_id: THIS
              size: classic
              title: Heating Override?
              timeout: 60000
              content:
                - name: p_minutes
                  label: >-
                    Duration (max 240 [4h]) - leave this blank to override until
                    next programmed set point.
                  selector:
                    number:
                      max: 240
                      min: 10
                      step: 20
                - name: p_entity
                  label: Room Selector
                  selector:
                    entity:
                      include_entities:
                        - climate.ant_office
                      filter:
                        domain: climate
                - name: p_setting
                  label: Target Temperature
                  selector:
                    number:
                      min: 16
                      max: 21
                      mode: slider
              right_button_action:
                action: script.heating_override

and this error is being returned from the script now.
Browser side

Failed to perform the action script/heating_override. expected float for dictionary value @ data['setpoint']

script side

Executed: 4 June 2025 at 10:36:21
Error: expected float for dictionary value @ data['setpoint']
Result:

So that is logical and makes more sense. It’s a streight type conversion issue now.

In the variables section try something like {{ iif(target_temperature|default(None), target_temperature, p_setting)|float}}

Sussed it - finally . . .

So the problem is that since the lovelace code I wrote is passing variable names as p_name these don’t match up what is in the script as you already tried to address in your variable translation code chunk.

What I tried was just to rename all the variable names on the popup / lovelace side to the same names as I had defined on the scipt side and then the script natively just assigns and substitutes them into the service call to set_override.

              content:
                - name: target_duration ## same name as field name in script.
                  label: >-
                    Duration (max 240 [4h]) - leave this blank to override until
                    next programmed set point.
                  selector:
                    number:
                      max: 240
                      min: 10
                      step: 20
                - name: target_room ## same name as field name in script.
                  label: Room Selector
                  selector:
                    entity:
                      include_entities:
.. redacted other room names ...
                        - climate.ant_office
                      filter:
                        domain: climate
                - name: target_temperature ## same name as field name in script.
                  label: Target Temperature

Now they are passed along and seem to work without any further issue every time.
It really helped understanding that all that was needed was to call the script in the action since that got rid of all the duplicates.

I need to reuse this learning for our exterior floodlights, 5 of, so the default is a minute but the “adults” can override for longer. There are quite a few duration problems we need to balance against being good plantary citizens so this will now allow that.

MANY MANY thanks for your valuable time and help good Sir.

Welcome to Home Assistant. You can do lots with scripting so it’s a good part to have learnt. Well done!

Exactly - for anyone else - the confusion really was that the hard coded names on the lovelace popup code side were passed correctly since they were defined as the same variable names in the fields on the script side.

When these were removed, the variable names were scraped from the “- name” keys and these were then not understood at the script side but, this was not clear until dcapslock pointed out that all that was needed was the call the script directly in the action: for the right click.

This removed the unneeded and confusing extra variables layer.