Allow duplicate top level keys in a package YAML

TL;DR below. I’m experimenting with using package yaml files to make it easier to work on a project base, instead of having to switch between multiple yaml files like sensor, template, etc. So far I’m liking it, but a small thing is bugging me.

A package allows you to order your yaml neatly, but still doesn’t give you the freedom I’d like inside the actual file, because it doesn’t allow double keys (keys being template and binary_sensor in the example below). For example, I’ve got this:

# ==============================
# HELPER ENTITIES
# ==============================

template:
  - sensor:
    - ...

binary_sensor:
  - platform: trend
    ...

# ==============================
# MAIN SECTION
# ==============================

template:
  - sensor:
    - ...

The first template sensor is truly a helper. It basically calculates a value to be used somewhere else based on some inputs, so it’s nothing more than a variable (or constant in this case). The second template is what I actually show in my frontend.

Sure, I could simply group everything together based on what type it is, but that just limits the flexibility of packages IMO. Since there’s already some processing involved in including with include_dir_named and include_dir_merge_named, and possibly the two list versions as well, would it be possible to handle double keys? I’m guessing that for multiple files, all the entries for a key get merged into one, so I expect it would be possible to grab the same key from a file multiple times too.

I understand that this is very much a ‘nice to have’ feature, so probably won’t get much attention, but IMO it would greatly improve how user-friendly YAML is. Ordering by type makes sense for machines, but not for humans.


TL;DR: When using include_dir_(merge_)named, I want to be able to use the same key multiple times, for ordering purposes. I feel like this should be possible, since keys from different files get merged.

I think I named the templates in my setup.
I am not in a place where I can see it, but try template <name>: and see if you can make more then.

It might be related to modern or legacy templates too, so you might have to read the documentation or ask here for more details.
This is probably just a hint, but it could be the one that leads you on the right track.

I remember seeing that somewhere too… good idea.
I tried implementing it, but my HA install seems to be a bit off today. I have one file loaded as a package. It used to be called ZigbeeRainMeter.yaml, then I renamed it to zigbee_rain_meter.yaml. For some reason, when I reload the YAMLs in HA now (did the check configuration button go away btw?), it still gives me an error for duplicate keys in ZigbeeRainMeter.yaml, even though that file doesn’t exist anymore.

I feel like the bottom error is worse though: “Mapping values are not allowed here in…”


I removed the include of the packages and restarted HA, that got rid of the old filename error. The only error I’m getting now is the one I was dreading before.

Line 17 in the configuration.yaml file:
image
The extra space in front of packages is intentional, otherwise you get an error saying that the integration ‘packages’ can’t be found.

And the contents of the only YAML file in the projectYamls folder:

# Package: zigbee_rain_meter.yaml
#
# RAIN METER BASED USING DOOR AND WINDOW SENSOR
# Standard tipping style rain meter, using the Aqara Door and Window sensor
# 
# Model:      Aqara MCCGQ11LM
#   Battery:  CR1632
#
# Entities used and not defined here:

# ==============================
# HELPER ENTITIES
# ==============================

# Calculate amount of rain in liter or mm per m² based on funnel size and volume required to tip the bucket
# Measurements: 1 liter tips bucket 152, 174 and 168 times. Assuming 170 is correct.
template helper:
  - sensor:
    - name: "Aqara Rain Meter - Rainfall per tick"
      unique_id: '0a7476cc-d6c1-40ba-8351-606518c3497sdfs'
      state_class: total
      unit_of_measurement: l/m²
      state: >
        {# Fill funnel radius and ticksPerLiter #}
        {% set funnelRadius = 0.055 %}
        {% set ticksPerLiter = 170 %}

        {# Don't change anything below #}
        {% set litersPerTick = 1 / 170 %}
        {{ (1 / (pi * ( funnelRadius**2 ))) * literPerTick }}

# --------------------------------------------------------------
# Convert rain meter ticks into rainfall intensity in mm/h / l/h
# --------------------------------------------------------------

binary_sensor:
  - platform: trend
    sensors:
      aqara_rain_sensor_rainfall_trend:
        friendly_name: "Rainfall trend"
        entity_id: sensor.aqara_rain_sensor_rainfall_today
        max_samples: 10
        sample_duration: 300

template helper2:
  - sensor:
    - name: "Aqara rain intensity"
      state_class: measurement
      device_class: precipitation
      unit_of_measurement: 'mm/h'
      unique_id: aqara_rain_sensor_rainfall_per_hour
      state: >-
        {% set rain_hour = ((state_attr('binary_sensor.aqara_rain_sensor_rainfall_trend', 'gradient') | float(0)) * 3600) | round(1, 'floor') %}
        {% if rain_hour >= 0 %}
          {{ rain_hour }}
        {% else %}
          {{ 0 }}
        {% endif %}

# ==============================
# SENSORS & BINARY SENSORS
# ==============================

sensor:
    # Flips ON | TODAY
    # Determines how many times the sensor was in the ON state today
  - platform: history_stats
    unique_id: "aqara_rain_sensor_flips_on_today"
    name: "Aqara rain sensor flips/on (Today)"
    entity_id: binary_sensor.aqara_dw_2_open_closed
    state: "on"
    type: count
    start: "{{ now().replace(hour=0, minute=0, second=0, microsecond=0) }}"
    duration:
      hours: 24
    # Flips OFF | TODAY
    # Determines how many times the sensor was in the OFF state today
  - platform: history_stats
    unique_id: "aqara_rain_sensor_flips_off_today"
    name: "Aqara rain sensor flips/off (Today)"
    entity_id: binary_sensor.aqara_dw_2_open_closed
    state: "off"
    type: count
    start: "{{ now().replace(hour=0, minute=0, second=0, microsecond=0) }}"
    duration:
      hours: 24
      
    # Flips ON | YESTERDAY
    # Determines how many times the sensor was in the ON state yesterday
  - platform: history_stats
    unique_id: "aqara_rain_sensor_flips_on_yesterday"
    name: "Aqara rain sensor flips/on (Yesterday)"
    entity_id: binary_sensor.aqara_dw_2_open_closed
    state: "on"
    type: count
    start: "{{ now().replace(hour=0, minute=0, second=0, microsecond=0) - timedelta(hours=24) }}"
    duration:
      hours: 24

    # Flips OFF | YESTERDAY
    # Determines how many times the sensor was in the OFF state yesterday
  - platform: history_stats
    unique_id: "aqara_rain_sensor_flips_off_yesterday"
    name: "Aqara rain sensor flips/off (Yesterday)"
    entity_id: binary_sensor.aqara_dw_2_open_closed
    state: "off"
    type: count
    start: "{{ now().replace(hour=0, minute=0, second=0, microsecond=0) - timedelta(hours=24) }}"
    duration:
      hours: 24

    # TEST TEST TEST
    # Flips TOTAL | TODAY
    # Determines how many times the sensor toggled today
  - platform: history_stats
    unique_id: "aqara_rain_sensor_flips_total_today"
    name: "Aqara rain sensor flips/total (Today)"
    entity_id: binary_sensor.aqara_dw_2_open_closed
    state:
      - "off"
      - "on"
    type: count
    start: "{{ now().replace(hour=0, minute=0, second=0, microsecond=0) - timedelta(hours=24) }}"
    duration:
      hours: 24

template main:
  - sensor:
      # Calculate rain volume for the current day based on flips on and off for today
    - name: "Aqara Rainfall (Today)"
      state_class: total_increasing
      device_class: precipitation
      unit_of_measurement: mm
      unique_id: aqara_rain_sensor_rainfall_today
      state: >-
        {% set count = (states('sensor.aqara_rain_sensor_flips_on_today') | int(0)) + (states('sensor.aqara_rain_sensor_flips_off_today') | int(0)) %}
        {% set mm = count * float(states('sensor.aqara_rain_meter_rainfall_per_tick')) %}
        {% if count >= 0 %}
          {{ mm | round (1) }}
        {% endif %}

      # Calculate rain volume for yesterday based on flips on and off for yesterday
    - name: "Aqara Rainfall (Yesterday)"
      state_class: total_increasing
      device_class: precipitation
      unit_of_measurement: mm
      unique_id: aqara_rain_sensor_rainfall_yesterday
      state: >-
        {% set count = (states('sensor.aqara_rain_sensor_flips_on_yesterday') | int(0)) + (states('sensor.aqara_rain_sensor_flips_off_yesterday') | int(0)) %}
        {% set mm = count * float(states('sensor.aqara_rain_meter_rainfall_per_tick')) %}
        {% if count >= 0 %}
          {{ mm | round (1) }}
        {% endif %}

I’m usually pretty good at figuring out what’s going on when there’s a log file, but the “mapping values not allowed” error seems to almost always be caused by a indentation error. I know it’s hard to see your own mistakes, but AFAICS there are no indentation errors.

Another update. I kept on looking for answers, and noticed that most people had:

homeassistant:
  packages: ...

instead of

...
  packages: ...

So I figured, why not try that:

homeassistant:
  packages: !include_dir_named configurationYamls/projectYamls

# Configure a default setup of Home Assistant (frontend, api, etc)
default_config:

group: !include configurationYamls/group.yaml
etc...

That had an odd effect. The binary_sensor, and 4 sensors are now available in HA, but everything that’s inside template suffix: still isn’t available, but there are no errors in my log. I still feel like this isn’t quite working as intended, so the FR still stands. It’d also be quite a bit easier to not have to append a suffix to get it to work.


Just realized I didn’t even post this in the FR section🤦🏻‍♂️

Closed. Discussion continues in the feature request posted above.