# Packages

Packages provide a way to bundle multiple related YAML sections into a reusable, parameterized unit. Unlike fragment‑level insertion, a package expands into full top‑level sections (such as things: or items:), sourced from either an external file or a same‑file template, and merges them into the current configuration.

# Purposes

  • Logical Grouping: Packages allow a Thing and its related Items, channels, and metadata to be defined together in one file, representing a complete, self‑contained device definition.

  • Reuse with Different Parameters: Through variable substitution, a single package can be instantiated multiple times with different values. This makes it easy to define many similar devices (for example, multiple sensors, switches, or rooms) from one shared structure.

# Package Syntax and Structure

Packages are declared in the main YAML file under the top‑level packages: section. Each entry defines a package ID and the source from which the package content is obtained.

packages:
  <package_id>: <package_source>
  <another_package_id>: <package_source>
  ...

# Key Components

  • Package ID: A unique identifier for the package (for example, package1). It may include spaces. The special variable ${package_id} resolves to this key inside the package content.

  • Package Source: A package can be created from either of the following sources:

    • !include (external file): Loads a separate YAML file and applies the package’s variable context to it. See the !include syntax options.

    • !insert (same‑file template): Expands a template defined under the main file’s templates: section. See the !insert syntax options.

    Both forms support parameterization through vars: and participate fully in package merging.

# Package Source Contents

  • Top‑Level Sections: Package sources can contain any combination of top‑level keys such as things: and items:.

  • Uniqueness: Because package sources can be referenced multiple times, use variable substitutions such as ${package_id} and unique vars: variables for entity UIDs in each invocation to avoid collisions.

  • Nesting: A package source can itself include other files or templates.

  • No version: key needed: Package sources do not define their own version. Only the main package file provides the version: key, and its value overrides anything that might appear in a package source.

# Package Example

main.yaml:

variables:
  broker: mqtt:broker:main

packages:
  livingroom-light: !include
    file: package/mqtt-light.inc.yaml
    vars:
      name: Living_Room_Light
      label: Living Room Light

  bedroom-light: !include
    file: package/mqtt-light.inc.yaml
    vars:
      name: Bed_Room_Light
      label: Bed Room Light

package/mqtt-light.inc.yaml:

things: !sub
  mqtt:topic:${package_id}:
    bridge: ${broker}
    channels:
      power:
        type: switch
        config:
          stateTopic: ${package_id}/state
          commandTopic: ${package_id}/set/state
      # ... other channels (brightness, color)

items: !sub
  ${name}_Power:
    type: Switch
    label: ${label} Power
    channel: mqtt:topic:${package_id}:power
  # ... more items for the light, e.g. brightness, color, etc.

Resulting YAML structure:

things:
  mqtt:topic:livingroom-light:
    bridge: mqtt:broker:main
    channels:
      power:
        type: switch
        config:
          stateTopic: livingroom-light/state
          commandTopic: livingroom-light/set/state
  mqtt:topic:bedroom-light:
    bridge: mqtt:broker:main
    channels:
      power:
        type: switch
        config:
          stateTopic: bedroom-light/state
          commandTopic: bedroom-light/set/state

items:
  Living_Room_Light_Power:
    type: Switch
    label: Living Room Light Power
    channel: mqtt:topic:livingroom-light:power
  Bed_Room_Light_Power:
    type: Switch
    label: Bed Room Light Power
    channel: mqtt:topic:bedroom-light:power

# Merge Behavior

# Final Top-Level Sections

A final top-level section is the fully expanded things:, items:, or other top‑level section of the configuration that openHAB receives after all packages, templates, includes, and merges have been applied. Entries you define directly under these sections can merge with package‑generated entries when their identifiers match; otherwise, they remain independent.

Note

The following example uses !insert, but the same merge rules apply to packages sourced from !include.

When a package is expanded, its contents are merged into the main YAML structure. You may optionally customize the resulting structure by overriding, adding, or removing elements defined in the package. This is done by redefining the elements you want to customize in the main file, which then appear in the final top‑level section.

# Default Merge Behavior

Source YAML File:

templates:
  number_item:
    items: !sub
      ${package_id}_Item:
        type: Number
        label: Package Label
        tags: [Measurement]
        metadata:
          stateDescription:
            config:
              min: 1
              pattern: '%.3f'
          widget:
            value: oh-card

packages:
  Number: !insert number_item

# This is the final top‑level `items:` section of the configuration
# The packages will merge into this section
items:
  Number_Item:
    label: Power Draw
    dimension: Power
    tags: [Power]
    metadata:
      stateDescription:
        config:
          max: 10

Result:

items:
  Number_Item:
    type: Number
    label: Power Draw
    dimension: Power
    tags:
      - Measurement
      - Power
    metadata:
      stateDescription:
        config:
          min: 1
          max: 10
          pattern: '%.3f'
      widget:
        value: oh-card

The way keys interact depends on their data type:

Data Type Behavior Description
Scalar Overwrite If the final top‑level section defines a scalar value at a specific path, that value replaces the scalar defined at the same path inside the package.
Map Merge Key‑value objects are merged key by key, recursively.
List Merge Arrays are concatenated together.

# Automatic Removal of Empty Values

During merging, empty structures are automatically stripped from the final configuration:

  • empty maps ({})
  • empty lists ([])
  • map keys whose value is null or an empty string

This keeps the resulting configuration clean and allows packages to define “catch‑all” defaults.

Example:

variables:
  icon: null   # default to avoid unknown‑variable warnings

icon: !sub ${icon}

Because icon evaluates to null by default, the entire icon: key is removed from the merged output unless the including file overrides it.

# How Package Merging Differs from YAML Merge Keys

Mappings from packages are merged recursively with the corresponding mappings in the final top‑level section of the configuration. This contrasts with standard YAML Merge Keys, which perform only shallow merges.

Merge Key (shallow merge):

# merge key:
targetkey:
  foo:
    bar:
      boo: baz
  <<: # merge `foo` into `targetkey`
    foo:
      bar:
        boo: waldo
        goo: fy
      qux: quux
# result — the merge key's foo mapping
# is ignored because foo already exists in main
targetkey:
  foo:
    bar:
      boo: baz

Package Merging (recursive merge):

# main file
targetkey:
  foo:
    bar:
      boo: baz

packages:
  anyid: !include packagefile.inc.yaml
# packagefile.inc.yaml
targetkey:
  foo:
    bar:
      boo: waldo
      goo: fy
    qux: quux
# result:
targetkey:
  foo:
    bar:
      boo: baz  # main file overrides matching keys
      goo: fy   # but includes additional keys...
    qux: quux   # from the package

Because the merge is recursive, you can customize keys at any depth in the mapping.

# Controlling Package Merge Behavior with Tags

Use these special YAML tags in the main file to override the default merge behavior:

# 1. The !replace Tag

Forces a replacement for maps or lists that would otherwise merge. Useful when you want to discard the package's list or map and start fresh.

# 2. The !remove Tag

Removes the corresponding key from the final configuration. Ideal for excluding specific entities or properties from a generic package.

Example:

main.yaml:

packages:
  Number: !include pkg/number.inc.yaml

# Custom overrides
items:
  Number_Item:             # Matches the resulting item name
    tags: !replace [Power] # Force overwrite, not merge
    metadata:
      stateDescription:
        config: !replace   # Force overwrite of this map
          format: "%.1f"
      widget: !remove      # Remove this key from the result

pkg/number.inc.yaml:

items: !sub
  ${package_id}_Item:
    type: Number
    label: Package Label
    tags: [Measurement]
    metadata:
      stateDescription:
        config:
          min: 1
          pattern: '%.3f'
      widget:
        value: oh-card

Result:

items:
  Number_Item:
    type: Number
    label: Package Label
    tags:                # tags from the package was !replaced, not merged
      - Power
    metadata:
      stateDescription:
        config:          # config from the package was !replaced, not merged
          format: '%.1f'

Usage Notes

  • !replace and !remove are only valid in the top-level of the main YAML file.
  • They are ignored if used inside a package source.
  • !remove removes the entire key; it cannot remove individual list items.
  • Use !replace to prune unwanted inherited map keys or list items.

# Strategic Use of Package IDs

Choose a Package ID that can also serve as a Thing UID fragment, Item name, or similar identifier. This avoids defining extra variables in your package source and lets you derive related identifiers directly from ${package_id}.

You can override ${package_id} in the vars: block if needed.

Example:

# main file
packages:
  Living_Room_Light: !include light.inc.yaml
  Kitchen_Light: !include light.inc.yaml
# light.inc.yaml package source
variables: !sub
  id: ${package_id|lower|replace('_', '-')}
  thing_uid: "mqtt:topic:${id}"
  item_name: ${package_id}
  label: ${package_id|replace('_', ' ')}

Resulting variables:

Variable Living_Room_Light Kitchen_Light
${package_id} Living_Room_Light Kitchen_Light
${id} living-room-light kitchen-light
${thing_uid} mqtt:topic:living-room-light mqtt:topic:kitchen-light
${item_name} Living_Room_Light Kitchen_Light
${label} Living Room Light Kitchen Light