Packages

When you have many ESPHome devices (or are producing and distributing them at scale), a common need tends to surface: configuration modularization. You’ll likely want to break your configuration into common (groups of) elements, building it into reusable pieces which can subsequently be used by many/all devices. Only unique pieces of your configuration remain in any given device’s YAML configuration file.

This can be accomplished with ESPHome’s packages feature.

All definitions from packages will be merged with your device’s main configuration in a non-destructive way. This allows overriding (parts of) configuration contained in the package(s). Substitutions in your main configuration will override substitutions with the same name in a package.

Dictionaries are merged key-by-key. Lists of components are merged by component ID (if specified). Other lists are merged by concatenation. All other configuration values are replaced with the later value.

ESPHome uses !include to “bring in” packages; this is a syntax brought over from Home Assistant’s YAML configuration directives.

Local Packages

Consider the following example where the author put common pieces of configuration (like Wi-Fi and API) into base files and then extends it with some device-specific configuration in the main configuration.

Note how the piece of configuration describing api component in device_base.yaml gets merged with the actions definitions from main configuration file.

# In config.yaml
packages:
  wifi: !include common/wifi.yaml
  device_base: !include common/device_base.yaml

api:
  actions:
    - action: start_laundry
      then:
        - switch.turn_on: relay

# any additional configuration...
# In wifi.yaml
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
# In device_base.yaml
esphome:
  name: ${node_name}

esp32:
  board: wemos_d1_mini32

logger:

api:
  encryption:
    key: !secret api_encryption_key

Remote/Git Packages

Packages can also be loaded from a Git repository by utilizing the correct configuration syntax. Substitutions can be used inside the remote packages which allows users to override them locally with their own substitution value.

Note

Remote packages cannot have secret lookups in them. They should instead make use of substitutions with an optional default in the packaged YAML, which the local device YAML can set using values from the local secrets.

# Git repo examples
packages:
  # shorthand form github://username/repository/[folder/]file-path.yml[@branch-or-tag]
  remote_package_shorthand: github://esphome/non-existant-repo/file1.yml@main

  remote_package_files:
    url: https://github.com/esphome/non-existant-repo
    files: [file1.yml, file2.yml]  # optional; if not specified, all files will be included
    ref: main  # optional
    refresh: 1d  # optional

Configuration variables:

For each package:

  • url (Required, string): The URL for the repository.

  • username (Optional, string): Username to be used for authentication, if required.

  • password (Optional, string): Password to be used for authentication, if required.

  • files (Required, list of strings): List of files to include.

  • ref (Optional, string): The Git ref(erence) to be used when pulling content from the repository.

  • refresh (Optional, Time): The interval at which the content from the repository should be refreshed.

Packages as Templates

Since packages are incorporated using the !include system, variables can be provided to them. This means that packages can be used as templates, allowing complex or repetitive configurations to be stored in a package file and then incorporated into the configuration more than once.

Packages may also contain a defaults block which provides subsitutions for variables not provided by the !include block.

As an example, if the configuration needed to support three garage doors using the gpio switch platform and the time_based cover platform, it could be constructed like this:

# In config.yaml
packages:
  left_garage_door: !include
    file: garage-door.yaml
    vars:
      door_name: Left
  middle_garage_door: !include
    file: garage-door.yaml
    vars:
      door_name: Middle
  right_garage_door: !include
    file: garage-door.yaml
    vars:
      door_name: Right
# In garage-door.yaml
switch:
  - name: ${door_name} Garage Door Switch
    platform: gpio
    # ...

Extend

To make changes or add additional configuration to included configurations, !extend config_id can be used, where config_id is the ID of the configuration to modify.

For example, to set a specific update interval on a common uptime sensor that is shared between configurations:

# In common.yaml
captive_portal:

sensor:
  - platform: uptime
    id: uptime_sensor
    update_interval: 1min
packages:
  common: !include common.yaml

sensor:
  - id: !extend uptime_sensor
    update_interval: 10s

Remove

To remove existing entries from included configurations, !remove [config_id] can be used, where config_id is the ID of the entry to modify.

For example, to remove a common uptime sensor that is shared between configurations:

packages:
  common: !include common.yaml  # see above

sensor:
  - id: !remove uptime_sensor

To remove captive portal for a specific device:

packages:
  common: !include common.yaml  # see above

captive_portal: !remove

See Also