Deprecation of legacy template entities in 2025.12

The original entity id should be in your gui and automations.

Yeah they are and look to be correct. For example:

I’m not so bothered about it, it’s hardly a major problem just my OCD about it being ā€œrightā€ or as right as it can be but, it may help others if they encounter similar issues.

I ordered up a transceiver module from Ali Express in any event, ESP32 stuff I have kicking about as I use a number of those now and hopefully that proves a far better solution long term.

The file is templates.yaml in the templates directory, which is definitely being picked up and parsed by home assistant.

templates.yaml contents:

  - sensor:
      - default_entity_id: sensor.tree_pollen
        name: |
          {% set pollen = states(''sensor.grass_pollen_count'')|int %}
          {% if pollen <15 %}  Low
          {% elif pollen < 89 %}  Moderate
          {% elif pollen < 500 %}  High
          {% elif pollen > 499 %}  Very High
          {% endif %}
        state: "{{states('sensor.tree_pollen_count') }}"
        availability: "{{ has_value('sensor.grass_pollen_count') }}"

The include in configuration.yaml

template: !include_dir_list templates/

If I rename the templates.yaml to be without a yaml extension, then the config check passes. So that tells me that it is being found and parsed.

I just noticed it’s also throwing this error in the log.

Logger: homeassistant.helpers.check_config
Source: helpers/check_config.py:217
First occurred: 5:47:59 AM (1 occurrence)
Last logged: 5:47:59 AM
Unexpected error validating config

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/check_config.py", line 217, in async_check_ha_config_file
    await config_validator.async_validate_config(hass, config)
  File "/usr/src/homeassistant/homeassistant/components/template/config.py", line 356, in async_validate_config
    template_config: TemplateConfig = await async_validate_config_section(
                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        hass, cfg
        ^^^^^^^^^
    )
    ^
  File "/usr/src/homeassistant/homeassistant/components/template/config.py", line 332, in async_validate_config_section
    validated_config = await _async_resolve_template_config(hass, config)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/template/config.py", line 319, in _async_resolve_template_config
    validate_trigger_format(hass, config, original_config)
    ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/template/config.py", line 154, in validate_trigger_format
    options = set(config_section.keys())
                  ^^^^^^^^^^^^^^^^^^^
AttributeError: 'NodeListClass' object has no attribute 'keys'

Hi everyone, I create a migration helper integration. It is in alpha stages. Please attempt to use it and report issues, thanks.

Open your Home Assistant instance and open a repository inside the Home Assistant Community Store.

11 Likes

Hello,
i have the problem to integrate my own pictures from the path www/moonphases.

erstes viertel.jpg
letztes viertel.jpg
neumond.jpg
vollmond.jpg
zunehmende sichel.jpg
zunehmender halbmond.jpg
abnehmende sichel.jpg
abnehmender mond.jpg

First i added the sample configuration and optimize it.
I read the whole thread, but i couldn“t find the solution. I tested a lot.

Old template:

sensor:
  - platform: template
    sensors:
      moon_phases:
        friendly_name: 'Mond'
        value_template: >
          {% set phases = { 'new_moon':'Neumond', 'waxing_crescent':'zunehmende Sichel', 'first_quarter':'erstes Viertel', 'waxing_gibbous':'zunehmender Halbmond', 'full_moon':'Vollmond', 'waning_gibbous':'abnehmender Mond', 'last_quarter':'letztes Viertel', 'waning_crescent':'abnehmende Sichel'} %}
          {% set phase = states('sensor.moon') %}
          {{ phases[phase] if phase in phases.keys() else 'unbekannte Mondphase' }}
        entity_picture_template: >
           {% set state = states('sensor.moon_phases').replace(' ','_')|lower %}
           {{ '/local/moonphases/{}.jpg'.format(state) }}

My new template:

template:
- sensor:
  - default_entity_id: sensor.moon_phases
    name: Mond
    state: >
      {% set phases = { new_moon:Neumond, waxing_crescent:zunehmende_Sichel, first_quarter:erstes_Viertel, waxing_gibbous:zunehmender_Halbmond, full_moon:Vollmond, waning_gibbous:abnehmender_Mond,
      last_quarter:letztes_Viertel, waning_crescent:abnehmende_Sichel}
      %} {% set phase = states( 'sensor.moon' ) %} 
      { phases[phase] if phase in phases.keys() else unbekannte Mondphase }
    picture: >
      {% set state = states( 'sensor.moon_phases' ).replace( ' ','_' )|lower %}
      { /local/moonphases/{}.jpg.format(state) }

Result of the template editor:

template:
- sensor:
  - default_entity_id: sensor.moon_phases
    name: Mond
    state: >
        
      { phases[phase] if phase in phases.keys() else unbekannte_Mondphase }
    picture: >
      
      { /local/moonphases/{}.jpg.format(state) }

What is wrong? It get a mdi icon and no picture.

Thank you for your help!

Best regards

You didn’t copy from the repair and you removed curly brackets

Petro,

Damn! That worked fantastically for me! Now I’ve got to go figure out why what I was given and what I tried before wasn’t working, which I will add should help a lot! I wish I had your skill in this area!

Can’t tell you how much I appreciate the effort. Thank you very VERY much!

1 Like

Thank you.
I added the following suggestion from home assistant.

  - default_entity_id: sensor.moon_phases
    picture: '{% set state = states(''sensor.moon_phases'').replace('' '',''_'')|lower
      %} {{ ''/local/moonphases/{}.jpg''.format(state) }}'
    name: Mond
    state: '{% set phases = { ''new_moon'':''Neumond'', ''waxing_crescent'':''zunehmende
      Sichel'', ''first_quarter'':''erstes Viertel'', ''waxing_gibbous'':''zunehmender
      Halbmond'', ''full_moon'':''Vollmond'', ''waning_gibbous'':''abnehmender Mond'',
      ''last_quarter'':''letztes Viertel'', ''waning_crescent'':''abnehmende Sichel''}
      %} {% set phase = states(''sensor.moon'') %} {{ phases[phase] if phase in phases.keys()
      else ''unbekannte Mondphase'' }}'

No picture.

Developer tools: yaml test: OK!

Template editor:

TemplateSyntaxError: expected token ā€˜,’, got ā€˜sensor’

I took your hass-migrate-template repo:

- sensor:
  - default_entity_id: sensor.moon_phases
    name: Mond
    picture: >-
      {% set state = states('sensor.moon_phases').replace(' ','_')|lower %} {{ '/local/moonphases/{}.jpg'.format(state)
      }}
    state: >-
      {% set phases = { 'new_moon':'Neumond', 'waxing_crescent':'zunehmende Sichel',
      'first_quarter':'erstes Viertel', 'waxing_gibbous':'zunehmender Halbmond', 'full_moon':'Vollmond',
      'waning_gibbous':'abnehmender Mond', 'last_quarter':'letztes Viertel', 'waning_crescent':'abnehmende
      Sichel'} %} {% set phase = states('sensor.moon') %} {{ phases[phase] if phase
      in phases.keys() else 'unbekannte Mondphase' }}

It is different to the home assistant suggestion.

Template editor: Ok
Developertools yaml test : ok

No picture only the icon

Best regards

So this was more complicated then it should’ve due to unclear instructions to us mere mortals. :slight_smile:
Took a lot of digging to understand that all of the split sensors and cover files (which we were taught are best practice) are in reality deprecated and now everything should go into a newly created template .yaml where you just paste the code from the repairs. Don’t get me wrong , that was a really nice touch and once I figured out based on the posts here that I have to remove in configuration.yaml the references to sensors.yaml ,covers.yaml a.s.o. and instead dump everything into the new templates.yaml it took no time. Thought for the future changes to start with and explain it simple like this in the change notice ,rather then dive directly into the code changes examples.
Also confusing was that all examples were only giving one sensor ,and of course ,we all have multiple template sensors .
The whole example with triggers I really didn’t understand where the triggers come into play .Do people have triggers into their configurations files ?

Yes, I use triggers in several templates.
In the new template format, I already divided everything into packages much earlier, which is why I avoided all these problems that I see here.

This is not true. None of those are depreciated. You can still use them for platforms other than template.

Triggered template sensors have been available for a while. See: https://www.home-assistant.io/integrations/template/#trigger-based-sensor---using-conditions-to-control-updates

@bluestar888 I do have a few triggers.

The actual instructions that @petro provided were pretty clear, just not fully comprehensive of all setups. Anyone with an advanced setup should already know how to format their *.yaml to suit.


Migration Guide

@petro has done a good job of creating a migration guide and the instructions placed in the repair notification:

https://www.home-assistant.io/integrations/template/#legacy-template-deprecation-migration-guide


Migration Helper

He has also created a helper tool:

https://github.com/Petro31/hass-migrate-template


Why is it so hard for some?

  • The complexity comes from HA’s flexibility and not locking anyone into a single method — which is exactly why many of us use HA.
  • People don’t read or forget about the HA Documentation. It has all the answers, but due to the sheer volume, we only read chunks when something breaks.
  • When first starting, many people (myself included) blindly follow tutorials and examples without truly understanding the format they are placing into their configuration.yaml file.
    (This is how learning usually begins.)
  • Once it’s working, they rarely touch it again unless something breaks — which breeds unfamiliarity over time.
  • YAML is like learning a foreign language. It’s hard when someone jumps straight into an advanced template without knowing the basics.
  • You’re learning YAML and Jinja templating at the same time.
  • A huge portion of users arrive from ecosystems like App Remote Control, Google, Alexa — Home Assistant is on a completely different level.
  • A large portion of us are not programmers.

Understanding Home Assistant Template Placement & Directory Include Modes

Home Assistant is extremely flexible — which is great — but it means there are multiple valid ways to include templates, sensors, automations, etc.

There are two major variables:

  1. Where the template is placed (direct, split file, or packages)
  2. Which !include_dir_* loader is used

Below is the full breakdown of all template placement methods and the four include directory loaders.


Summary: All Template Placement Options

Method File Structure
Direct configuration.yaml template: …
Split configuration.yaml + templates.yaml template: !include
Packages (Flat) packages/*.yaml template: under each file
Packages (Named) packages/*.yaml filename: wrapper + template:

1. Direct in configuration.yaml

The simplest option.
Define the template directly at the root level.

homeassistant:
  # general settings

template:
  - trigger:
      - platform: event
        event_type: something_happened
    sensor:
      - name: Example Sensor
        state: "{{ trigger.event.data.value }}"

2. Split into templates.yaml

Useful for keeping configuration.yaml clean.

configuration.yaml

homeassistant:
  # other settings

template: !include templates.yaml

templates.yaml

(Do NOT include homeassistant: here.)

- trigger:
    - platform: event
      event_type: something_happened
  sensor:
    - name: Example Sensor
      state: "{{ trigger.event.data.value }}"

3. Packages — Flat Style (!include_dir_list)

This is the cleanest and most modern style.
Each file becomes an item in a merged list.

configuration.yaml

homeassistant:
  packages: !include_dir_list packages

packages/example.yaml

template:
  - trigger:
      - platform: event
        event_type: something_happened
    sensor:
      - name: Example Sensor
        state: "{{ trigger.event.data.value }}"

No wrapper keys needed.


4. Packages — Namespaced Style (!include_dir_named)

Each file becomes its own namespace based on the filename.

configuration.yaml

homeassistant:
  packages: !include_dir_named packages

packages/example.yaml

example:
  template:
    - trigger:
        - platform: event
          event_type: something_happened
      sensor:
        - name: Example Sensor
          state: "{{ trigger.event.data.value }}"

Home Assistant sees:

packages:
  example:
    template: ...

Understanding All !include_dir_* Options

These determine how multiple files inside a directory are combined.


!include_dir_list

:heavy_check_mark: Returns a list
:heavy_check_mark: Each file → one list entry
:heavy_check_mark: Files loaded in alphanumeric order

Great for:

  • flat packages
  • scripts
  • scenes
  • groups
  • any config expecting a list

!include_dir_named

:heavy_check_mark: Returns a dictionary
:heavy_check_mark: { filename_without_ext : file_content }
:heavy_check_mark: Good for grouping

Great for:

  • namespaced packages

!include_dir_merge_list

:heavy_check_mark: Each file must contain a list
:heavy_check_mark: All lists merge into one large list

Great for:

  • splitting automations across multiple files
  • splitting scripts or scenes while keeping a single list

!include_dir_merge_named

:heavy_check_mark: Each file must contain a dictionary
:heavy_check_mark: All dictionaries merge into one big combined dictionary

Great for:

  • template sensors
  • groups
  • input_* entities
  • any named collection of items

Summary Table (All Methods)

Loader Output Requirements Best Use Example Behaviour
!include_dir_list List Any content Flat packages, scripts, groups Each file becomes a list item
!include_dir_named Dict Any content Namespaced packages {filename: content}
!include_dir_merge_list List Files must contain lists Automations/scripts split across files All lists merged
!include_dir_merge_named Dict Files must contain dicts Template sensors, groups All dicts merged
11 Likes

HI,

I’e had trouble since the new Core Update with my Shelly garage door template. It shows the state as unkown and I cannot figure out how to fix it. I know it says Binary sensors do not work anymore but I’m unsure of how to change my shelly switch to a non binary sensor.

Any help is appreciated. Here is my old configuration.yaml code followed by the new one

old template:

template:
  - sensor:
      - name: "Garage Door"
        state: >
          {% if is_state('binary_sensor.garage_sensor', 'on') and is_state('switch.garage_door', 'on') %}
            opening
          {% elif is_state('binary_sensor.garage_sensor', 'off') and is_state('switch.garage_door', 'on') %}
            closing
          {% elif is_state('binary_sensor.garage_sensor', 'off') %}
            open
          {% else %}
            closed
          {% endif %}
 
cover:
  - platform: template
    covers:
      garage_door:
        device_class: garage
        friendly_name: "Garage Door"
        value_template: "{{ states('sensor.garage_door') }}"
        open_cover:
          - condition: state
            entity_id: sensor.garage_door
            state: "closed"
          - service: switch.toggle
            target:
              entity_id: switch.garage_door
        close_cover:
          - condition: state
            entity_id: sensor.garage_door
            state: "open"
          - service: switch.toggle
            target:
              entity_id: switch.garage_door
        stop_cover:
          service: switch.toggle
          target:
            entity_id: switch.garage_door
        icon_template: >-
          {% if is_state('cover.garage_door', 'opening') or is_state('cover.garage_door', 'closing') %}
            mdi:garage-alert
          {% elif is_state('cover.garage_door', 'open') %}
            mdi:garage-open
          {% else %}
            mdi:garage
          {% endif %}

new template that was pre generated:

template:
- cover:
  - device_class: GARAGE
    open_cover:
    - condition: state
      entity_id:
      - sensor.garage_door
      state: closed
      match: all
    - target:
        entity_id:
        - switch.garage_door
      action: switch.toggle
    close_cover:
    - condition: state
      entity_id:
      - sensor.garage_door
      state: open
      match: all
    - target:
        entity_id:
        - switch.garage_door
      action: switch.toggle
    stop_cover:
    - target:
        entity_id:
        - switch.garage_door
      action: switch.toggle
    default_entity_id: cover.garage_door
    icon: "{% if is_state('cover.garage_door', 'opening') or is_state('cover.garage_door',
      'closing') %}\n  mdi:garage-alert\n{% elif is_state('cover.garage_door', 'open')
      %}\n  mdi:garage-open\n{% else %}\n  mdi:garage\n{% endif %}"
    name: Garage Door
    state: '{{ states(''sensor.garage_door'') }}'

Where did you get that information?

I’m sure it said it when my template said it couldn’t use binary sensors? Maybe I read or understood it incorrectly

Since I have a lot of sensors on the repair list, I’m wondering if there’s a textual version of that list somewhere?
I have two HomeAsistants and I noticed on the one with fewer sensors to repair that some sensors disappear from the repair list after I repair one and restart HomeAsistant.

Only the ones you repaired should disappear. Did you accidentally delete or comment out too much of your config?

Sorry I spoke too soon.

Although the update migrated most of my my sensors, I’m having problems with several which I will list separately. Hopefully after I get some help with the 1st, I’ll be able to figure out the others.

I’m using the Waste collection integration in my config.yml.

Old code was:

waste_collection_schedule:
  sources:
    - name: landkreis_kusel_de
      args:
        ortsgemeinde: Erdesbach
      calendar_title: "Trash Pickup"
      customize:
        - type: "Glas"
          alias: "Glass"
          icon: mdi:sack-outline
        - type: "Bioabfall"
          alias: "Bio"
        - type: "Restmüll"
          alias: "Trash"
        - type: "LVP-AbfƤlle"
          alias: "Yellow bag"
          icon: mdi:recycle
        - type: "Papier"
          alias: "Paper Bin"
          icon: mdi:recycle
        - type: "Umweltmobil"
          alias: "Recycling van"
          icon: mdi:truck
  separator: " and "

  - platform: waste_collection_schedule
    name: Trash
    details_format: upcoming
    count: 1
    leadtime: 2
    value_template: '{{value.types|join(", ")}} {% if value.daysTo == 0 %}Today{% elif value.daysTo == 1 %}Tonight{% else %}in {{value.daysTo}} Days{% endif %}'

  - platform: waste_collection_schedule
    name: Trash_announcement
    value_template: '{{value.types|join(" and ")}}'

seems - platform reference to the waste_collection_shedule isn’t correct now.

Help?!

They are not template sensor platforms. Don’t move them.