On ways of improving YAML configurations (anchors & secrets)

Yesterday I spent plenty of time reading some very interesting articles by Thomas Loven and applying tricks described.
I was mainly interested in a possibility of using constants across various configuration files.
One of the examples - I want to have an input_select with some string constants as options and in a template sensor I need to know which one is selected and act accordingly (or I want to have a binary_sensor that is on when a particular option is selected).
The easiest way is to use strings where required (hardcode) but the code quickly becomes difficult to maintain/change.
One of the options I came across long time ago is to define constants in secret.yaml and then write !secret my_const where I need it. The problem is it doesn’t work in templates. It’s possible to overcome this restriction by creating a template sensor with

value_template: !secret my_const

The issue with this approach is it requires extra measures if you want to use that template sensor’s state (that very my_const) in another template sensor as during HA start your const template sensor might not be created yet when you try accessing its state. It’s manageable but the whole thing gets too complicated for no reason really…
Anyway, I read some docs and examples, went through this forum and realised it might be useful to summarise the experience with anchors as currently it’s a bit spreaded out/not very obvious. So (briefly as it’s already tl;dr) on YAML anchors (just anchors):

  1. They cannot be used in templates.
  2. They must be declared and used in *the same .yaml file.
  3. They can represent any valid YAML structure.

What does it give us?
(1) That’s sad, but sometimes it’s still useful, I’ll show an example below.
(2) No !include will help. That mean that uless you have everything in your configuration.yaml(unrealistic), the most benefits it gives you when used in packages. Anywhere else it’s almost useless, you can do something simple like this but no more:

- platform: template
  sensors:
    ## groung floor ##
    pir_ground_floor_hall_last_triggered:
      value_template: "{{ states('input_datetime.pir_ground_floor_hall') }}"
      <<: &icon
        icon_template: mdi:calendar-clock

    pir_ground_floor_reception_last_triggered:
      value_template: "{{ states('input_datetime.pir_ground_floor_reception') }}"
      <<: *icon

just because it’s difficult to find a place where you can declare it without getting errors from yaml loader.

Packages are different exactly because everything is in one file (and that’s why anchors work in Lovelace configs if you don’t split it up). You just use a dummy place like

homeassistant:
  customize:
    package.node_anchors:
      const_preset_normal: &const_preset_normal 'normal'

at the top of your package file and it works. However, it’s still limiting you by boundaries of 1 file so no way to have global_anchors.yaml and include it where you need it.
But with packages you can make it a little bit less dependent on secrets.yaml - instead of the approach with const template sensors I use this

homeassistant:
  customize:
    package.node_anchors:
      const_preset_normal: &const_preset_normal 'normal'
      template_normal: &template_normal "{{ is_state('input_select.central_heating_preset', 'normal') }}"

      const_preset_night: &const_preset_night 'night'
      template_night: &template_night "{{ is_state('input_select.central_heating_preset', 'night') }}"

input_select:
  central_heating_preset:
    name: preset
    options:
      - *const_preset_normal
      - *const_preset_night

binary_sensor:
  - platform: template
    sensors:
      central_heating_preset_normal:
        value_template: *template_normal

      central_heating_preset_night:
        value_template: *template_night

I still have to hard-code string constants normal and night but they’re in one place so it’s easy to change them/keep synchronised and I can use them in as many places as I need.

The reason we don’t have access to anchors declared in another file is the way HA processes yaml files (as all !secret, !include etc are not part of YAML, it’s HA). I’m not convinced that it’s impossible to implement such a feature. And maybe it’s relatively easy to write a post-processor that loads configuration.yaml, creates a list of files to process, builds a list of anchor definitions and then performs necessary substitutions.

(3) You can use anchors to substitute a value or a piece of a dictionary (I use both approaches in my packages example).

tl;dr #2
I just summarised my and other’s experience with YAML anchors here and added some comments where I thought it wasn’t clear enough.
It’s easy to find some useful topics here (like this or this).
And I’m happy to discuss the matter further or learn something new I have missed in this write-up.

2 Likes

Not true. This simply doesn’t work in all of yaml. Inside or outside home assistant… which leads to the next point…

It’s not impossible. But it would come from the loader library that home assistant uses. I.E. Outside home assistant. For what it’s worth, I believe some dude wrote an extension for this ability but the api looks completely different. It could be a large change. But again, it’s not in a official library, so it would be up to the devs whether they implement it or not.

I love the style :wink:
Afaik, YAML has nothing to do with files at all, it’s ‘a stream’. It’s up to a loader to provide that stream. And there are several possible ways of creating that stream - we can process each file as a separate stream or combine some of them. I haven’t looked into the code though.

tbh I don’t know if they use a third-party lib for that (they most likely do). Anyway, I just expressed my understanding of things with YAML in HA.

I think I stumbled upon that discussion some time ago. Looks like I can just use the extended ruamel as a post-processor I described above. That’s not ideal but…

I was referring to the python library, yaml. Which is what home assistant uses.

I don’t think they would, which is why I was pointing out that it’s not in the yaml library.

yeah, got it.
thanks for the link, I’ll definitely give it a go locally.

And don’t miss understand me, i’m not shooting down your idea. I want this purely for lovelace alone. That’s why I was looking into recently.

no worries at all. as I pointed out, for a single yaml file it should work just fine.
as it rarely happens outside Lovelace, I investigated further to see what options are available.

So with lovelace, you can use the decluttering card, but that ‘can’ break functionality on cards like button card. The decluttering templates can be included and they can also be included in other files.

EDIT It sometimes doesn’t declutter anything too

Well, my situation is completely opposite. I haven’t looked into that decluttering card at all as I a) try not using non-standard components in any form as it means more support and less stability in general (which I cannot afford atm) and b) my Lovelace works in GUI mode so I don’t see serious reasons in editing Lovelace config manually.

You should reconsider lovelace cards as ‘non-standard-components’. The ui is so decoupled from the backend that everything just works regardless of the UI. Custom integrations on the other hand…

I probably will pretty soon. One of the goals is to create a card for a custom alarm (as currently it’s a separate view but many want it as a card).