Extending a component defined in a package

Hello, I’m trying to “extend” the configuration of a component defined a package. Something like:

# package.yaml
time:
  - platform: homeassistant
    id: ha_time
    on_time:
      - seconds: 10
        then: ...

# main.yaml
packages:
  my_package: !include package.yaml

time:
  # goal: add another trigger  to the ha_time component
  - platform: homeassistant
    id: ha_time
    on_time:
      - seconds: 20
        then: ...

The packages documentation states:

All definitions from packages will be merged with your main config in non-destructive way so you could always override some bits and pieces of package configuration.
Dictionaries are merged key-by-key. Lists of components are merged by component ID if specified. Other lists are merged by concatenation. All other config values are replaced with the later value.

So I was hoping that the two ha_time components would be merged (same component ID) and their on_time lists would be concatenated. However, esphome just complains about a redefined ID:

ID ha_time redefined! Check time->0->id

So what is the meaning of "Lists of components are merged by component ID if specified"? Is there a way to extend the configuration of a component defined in a package?

3 Likes

I am facing the same error when trying to “override” a binary sensor. Is the documentation incorrect?

Yeah I just ran into the same thing and don’t understand what the documentation is hinting at there, and the examples after it don’t include an example of this. In my case I’ve got this:

# In pkg/sonoff-s31-plug.yaml, just a snippet of the whole
switch:
  - platform: gpio
    name: ${device_name_upper} Relay
    pin: GPIO12
    id: relay
    restore_mode: RESTORE_DEFAULT_ON

# In s31-fan.yaml
substitutions:
  device_name: "s31-fan"
  device_name_upper: S31 Fan

packages:
  common: !include pkg/common.yaml
  s31-plug: !include pkg/sonoff-s31-plug.yaml

switch:
  - platform: gpio
    id: relay
    pin: GPIO12
    restore_mode: RESTORE_DEFAULT_OFF

And end up with an ID relay redefined! Check switch->0->id. error when validating the config. I tried putting the override under a package listed after the s31-plug package as well, but it has a slightly different but still “ID redefined” error result as putting the attempted override at the top level.

I dug into this and it looks like the documentation in question was added recently but that the pull request it was attempting to document never actually landed, so yeah this doesn’t seem to be possible today. See Merging lists of components by ID does not actually work but is documented · Issue #3932 · esphome/issues · GitHub.

1 Like

By the way, I ended up using substitutions to achieve what I wanted. There wasn’t a clear example of substitutions from a package getting overridden so I wasn’t sure it would work, but it appears to work as you’d expect where you can define a default in the package and optionally override it in the including file. Perhaps that will work for y’all’s use cases?

# In pkg/sonoff-s31-plug.yaml
substitutions:
  relay_restore_mode: RESTORE_DEFAULT_ON

# In s31-fan.yaml:
substitutions:
  relay_restore_mode: RESTORE_DEFAULT_OFF

sbrocket, first of all, thank you for your investigation, for opening the bug and commenting on the unmerged PR. Good community contribution. :+1:
Substitutions are ok as long as the logic is pretty basic.

Just wanted to say that I ended up here, and followed @sbrocket’s issues, which were merged. Turns out, to extend, you need to explicitly state so using the !extend keyword. Hopefully this helps others… :)… Now I just need to figure out how to remove a component from a package.

Example:

button:
  - id: !extend my_id
    name: Overriden name
5 Likes

@LordMike : it works, thanks a lot for the info! I actually like that you have to explicitly declare that you are extending an existing component, to avoid errors.

Let us know if you find a solution for removing parts of the package, that would be awesome.

I noted this in an issue I created for the topic.

I found that for my specific use case, I could mark the button as internal. This hides it from Home Assistant. It’s not perfect, but it’s very close.