# 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’stemplates: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:anditems:.Uniqueness: Because package sources can be referenced multiple times, use variable substitutions such as
${package_id}and uniquevars: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 theversion: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
nullor 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
!replaceand!removeare only valid in the top-level of the main YAML file.- They are ignored if used inside a package source.
!removeremoves the entire key; it cannot remove individual list items.- Use
!replaceto 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 |