# 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:.
These sections may come from an external file or a same‑file template.
They are then merged 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 such as sensors, lights, or switches from one shared template.
# 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 package ID is a unique identifier for the package. 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 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.Deep Nesting: A package source may itself include other packages. When a package references another package via
packages:inside an included file, it creates an inheritance chain. Properties merge downward sequentially from the deepest core file out to the final main file.
# 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:
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:
${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
Package merging uses the same recursive merge mechanism that is documented in the Deep Merge reference. Scalar, map, and list interactions during package expansion follow the unified deep‑merge rules.
# Default Merge Behavior
Source YAML File:
templates:
number_item:
items:
${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
# Package Merge Tags
Package consumers can use !default, !replace (or !freeze), and !remove to control how package values interact with main‑file values.
The deep‑merge documentation contains the authoritative, consolidated definitions and examples for these tags.
Use the deep‑merge reference when you need the precise semantics for the merge behavior during package merging.
# How Package Merging Differs from YAML Merge Keys
Mappings from packages are merged recursively with the corresponding mappings in the final top‑level section. This differs from standard YAML merge keys, which perform only shallow merges.
# 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:
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 |
# Limitations: Top-Level Merge Keys & Deep Merges in packages:
Top-level YAML merge keys (<<:) and deep merge directives (!deep <<:) are not supported directly inside the packages: map.
Each package must be declared explicitly as a direct key under packages:.
The following patterns are not supported:
packages:
# Shallow merge in packages is NOT supported
<<: !include common-packages.yaml
# Deep merge in packages is NOT supported
!deep <<: !include common-packages.yaml
In these forms, the merge key tries to inject package declarations into packages: itself.
The engine extracts package declarations before structural composition occurs, so merge keys cannot be used to generate top-level package mappings.