Substitution

Hi,

In the ESPHome integration, one of the neat feature was the ability to use “substitutions”, which allowed me to write reusable code with the ability to change parameters and not touch the actual code. Here is one such example:

#The substution block has all the configurable details
substitutions:
  name_of_board: alarm-a
  ota_password: !secret ota_alarm_A
  wifi: !secret ssid
  wifi_password: !secret ssid_password
  ap_point: !secret ssid_alarm_A
  ap_point_password: !secret ssid_alarm_A_password
  name_1: "Window-1"
  name_2: "Window-2"
  name_5: "Window-5"
  name_6: "Window-6"
  name_7: "Window-7"
  name_RX: "Window-8"
  name_9: "Window-9"
  name_10: "Window-10"

############################################
#YOU SHOULD NOT NEED TO EDIT BELOW THIS LINE
############################################
esphome:
  name: $name_of_board
  platform: ESP8266
  board: nodemcuv2

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:
  password: $ota_password
wifi:
  ssid: $wifi
  password: $wifi_password
  fast_connect: True

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: $ap_point
    password: $ap_point_password

captive_portal:

binary_sensor:
  - platform: gpio
    name: $name_1
    device_class: window
    pin:
      number: D1
      mode: INPUT_PULLUP
  - platform: gpio
    name: $name_2
    device_class: window
    pin:
      number: D2
      mode: INPUT_PULLUP
  - platform: gpio
    name: $name_5
    device_class: window
    pin:
      number: D5
      mode: INPUT_PULLUP
  - platform: gpio
    name: $name_6
    device_class: window
    pin:
      number: D6
      mode: INPUT_PULLUP
  - platform: gpio
    name: $name_7
    device_class: window
    pin:
      number: D7
      mode: INPUT_PULLUP
  - platform: gpio
    name: $name_RX
    device_class: window
    pin:
      number: RX
      mode: INPUT_PULLUP
  - platform: gpio
    name: $name_9
    device_class: window
    pin:
      number: 9
      mode: INPUT_PULLUP
  - platform: gpio
    name: $name_10
    device_class: window
    pin:
      number: 10
      mode: INPUT_PULLUP

I tried using substitutions in “configuration.yaml” file but it says “integration not found”.

Is there a reason why it works in YAML files for ESPHome but not under rest of HA?

1 Like

Did you mean this?

Not really, the substitutions are more local (specific to a single configuration file) and just provide an easy way to reuse a working configuration and manage it.

The example I quoted above has been used 6+ time as I have multiple boards in my alarm system. For each new board, I don’t have to tinker the code and only need to change few logical values in the “substitutions” block.

The simplest reason is because they are two separate software products. How any software employs YAML is up to the software developer’s discretion.

If you need substitute in configuration.yaml, you can consider using !secret (even though it is not a secret)-

That’s an old post but will answer for clarity.
This should work:

binary_sensor:
  - platform: gpio
    name: "${name_1}"

Check this configuration: Home-Assistant-Configuration/dishwasher.yaml at 04bacf53c37290ed22dd831d9359a70e44e01fcb · jonathanadams/Home-Assistant-Configuration · GitHub

Thanks for updating and yes the substitution feature started working and I’ve been using it for a while and it has made life a lot easier. Here is the re-work of inital code I posted in 2021!

#The substitution block has all the configurable details
substitutions:
  name_of_board: alarm-a
  ota_password: !secret ota_alarm_A
  api_password: !secret alarm_A_api_password
  api_encryption_key: !secret alarm_A_api_encryption_key
  device_class_type: "window"
  name_1: "Window-A1"
  name_2: "Window-A2"
  name_5: "Window-A5"
  name_6: "Window-A6"
  name_7: "Window-A7"
  name_RX: "Window-A8"
  name_9: "Window-A9"
  name_10: "Window-A10"
  internal_1: "false"
  internal_2: "false"
  internal_5: "false"
  internal_6: "false"
  internal_7: "false"
  internal_RX: "true"
  internal_9: "true"
  internal_10: "false"
  ip_address: !secret ip_address_alarm_A

############################################
#YOU SHOULD NOT NEED TO EDIT BELOW THIS LINE
############################################
packages:
  wifi_settings: !include common/basic_components.yaml
  basics: !include common/basic_sensors.yaml
  device_settings: !include common/nodemcu.yaml
1 Like

Hi, where exactly do you put the substitutions: block ?
If I add to configuration.yaml like:

substitutions:
  shelly1: "shellies/shellyem-62724691ABED"

I get the following error:

Integration error: substitutions - Integration 'substitutions' not found.

Just want to ping this thread again in case I am missing some new feature that allows substitutions in HA YAML files. Coming from ESPHome, I too miss the ability to use the substitution definition to create essentially as set of variables to be used in that YAML file.

I’ve been slowly moving things from my configuration.yaml into the packages directory and would love to be able to create a template of sorts like we can in ESPHome and then just change the substitution data to reuse that code. Otherwise, copying a configuration requires find and replace, which is error prone.

There is a feature request at WTH - add substitutions ability to automations / scripts

Go ahead and vote for it.

How can I use substitutions in ESPHome from a deeper nested attribute, e.g. to only change the brightness level:

script:
  - id: reset_led
    then:
      - if:
          condition:
            - switch.is_on: use_wake_word
            - switch.is_on: use_listen_light
          then:
            - light.turn_on:
                id: led
                blue: 100%
                red: 100%
                green: 0%
                brightness: 100% // Only substitute this one

Thanks!

Try this:

substitutions:
  brightness_level: 100 # put whatever number you want here

script:
  - id: reset_led
    then:
      - if:
          condition:
            - switch.is_on: use_wake_word
            - switch.is_on: use_listen_light
          then:
            - light.turn_on:
                id: led
                blue: 100%
                red: 100%
                green: 0%
                brightness: $brightness_level
1 Like

But that means I also have to repeat the whole script part in the config? I’m a bit confused what the difference is then between just giving a different value to brightness and using the substitution value?

I assume the full (M5Stack Atom) config is somewhere deep in the ESPhome add-on which I cannot acces and the config I provide only sets the deltas.

Keep the substitution in the device file, but put the script part in its own file to include it in other device files. Is that what you want?

Look at the extend section here: Configuration Types — ESPHome.

substitution variable increases the re-usability of code.

If you create a file called “light_set_up.yaml” in the esphome folder:

script:
  - id: reset_led
    then:
      - if:
          condition:
            - switch.is_on: use_wake_word
            - switch.is_on: use_listen_light
          then:
            - light.turn_on:
                id: led
                blue: 100%
                red: 100%
                green: 0%
                brightness: $brightness_level

Then you can create multiple instances of light devices (with unique names) such as “light-01.yaml” as follows:

substitutions:
  brightness_level: 100 # put whatever number you want here
packages:
  setup: !include light_set_up.yaml

So if you have 10 lights, you only need to write this code once and just pass value to each light.

If you are trying to use this script as a means to change the value of brightness when operating the lights, it will not work because each light in the above code will have a hard coded value for brightness. If you are trying to control the brightness of this light from HA then you may also want to use this:

same for me

Another example, I have an automation for

  • a washing machine with a powerplug_wm . I have the very same automation for
  • a dryer with a power_plug_dr and
  • a dishwasher with a powerplug_dw

substitutions would help here.

You should look at “field” and “variables” that I’ve started using in my scripts. It is not quiet “substitution” but fairly efficient and useful in maintenance of code base.

Here is one example where I have a dashboard of toggles that can be turned on/off for creating a dynamic notification recipient list.

HTH

dynamic_email_opt_in:
  alias: Dynamically opt in for notifications
  mode: single
  fields:
    dynamic_opt_in_event:
      required: true
  variables:
    a: !secret pankaj_email
    b: !secret pankaj_cell
    default_recepient: !secret pankaj_email
    users:
      - !secret pankaj_cell
      - !secret pankaj_email
    dishwasher:
      - input_boolean.pankaj_email_dishwasher
      - input_boolean.pankaj_sms_dishwasher
    laundry_washer:
      - input_boolean.pankaj_email_laundry_washer
      - input_boolean.pankaj_sms_laundry_washer
    laundry_dryer:
      - input_boolean.pankaj_email_laundry_dryer
      - input_boolean.pankaj_sms_laundry_dryer
    mapper:
      input_boolean.pankaj_email_dishwasher: "{{ a }}"
      input_boolean.pankaj_email_laundry_dryer: "{{ a }}"
      input_boolean.pankaj_email_laundry_washer: "{{ a }}"
      input_boolean.pankaj_sms_dishwasher: "{{ b }}"
      input_boolean.pankaj_sms_laundry_washer: "{{ b }}"
      input_boolean.pankaj_sms_laundry_dryer: "{{ b }}"
    targets: |
      {% if ( dynamic_opt_in_event == "dishwasher") %}
        {% set ns = namespace( emails=[] ) %}
        {% set bools = dishwasher | select('is_state', 'on') | list %}
        {% for my_bool in bools %}
        {% set ns.emails = ns.emails + [ mapper.get(my_bool) ]%}
        {% endfor %}{{ ns.emails }}
      {% else %}
        {{default_recepient}}
      {% endif %} 

  sequence:
  - service: notify.smtp_1_gmail
    data:
      title: Dynamic opt-in list
      message: "Test-else-2"
      target: "{{ targets }}"