Skip to content
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
66aae29
[yamlcomposer] New Yaml Composer add-on
jimtng Feb 26, 2026
fe1482a
improve watch logic around deletion and load time
jimtng Mar 6, 2026
1f4a8a4
address copilot review
jimtng Apr 18, 2026
3ad94cb
fix markdownlint issues
jimtng Apr 18, 2026
130106f
adjust parseMapParameters to pass test
jimtng Apr 18, 2026
b7f1c93
update doc on package list merge mechanism
jimtng May 26, 2026
245fd1e
remove unused MergeKeyProcessor
jimtng May 26, 2026
4b0ade0
defend against null return from TemplateError.getMessage()
jimtng May 26, 2026
0dab0c1
address copilot review
jimtng May 26, 2026
3653ed0
add dependencies in features.xml, fix add-on spelling
jimtng May 26, 2026
2aa6e96
update itest.bndrun
jimtng May 27, 2026
e558edb
add snakeyaml-engine in features.xml
jimtng May 27, 2026
c981f9d
fix javadoc in IncludeProcessor
jimtng May 27, 2026
7efe085
Clarify the documentation of nested packages, !remove and !replace ta…
jimtng May 27, 2026
5bbeb0c
wrap single line ifs with braces
jimtng May 27, 2026
2bae740
add snakeyaml-engine to itest.bndrun
jimtng May 27, 2026
e07c85b
improve include logging
jimtng May 27, 2026
21ac9ea
fix javadoc for RecursiveTransformer#register()
jimtng May 27, 2026
16ec359
add support for merge keys at the top-level of the templates section
jimtng May 27, 2026
1f1f2b4
document limitation of merge keys in packages
jimtng May 27, 2026
36c3192
Prevent !replace/!remove from being processed in the template inserti…
jimtng May 28, 2026
a4ab3f3
Add note that using the same file as main file and as include is not …
jimtng May 28, 2026
71dc5ef
warn on null package keys
jimtng May 28, 2026
280648c
narrow down bnd.importpackage
jimtng May 28, 2026
da5183e
minor clean up caught by copilot
jimtng May 28, 2026
3e750fd
apply copilot suggestions.
jimtng May 28, 2026
daade99
only write readme.txt when we had to create the composed dir
jimtng May 28, 2026
51a03e1
spotless
jimtng May 28, 2026
a1be4ed
change source/output root for clean up
jimtng May 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@
/bundles/org.openhab.io.metrics/ @pravussum
/bundles/org.openhab.io.neeo/ @morph166955
/bundles/org.openhab.io.openhabcloud/ @kaikreuzer
/bundles/org.openhab.io.yamlcomposer/ @jimtng
/bundles/org.openhab.persistence.dynamodb/ @ssalonen
/bundles/org.openhab.persistence.influxdb/ @lujop
/bundles/org.openhab.persistence.inmemory/ @J-N-K
Expand Down
13 changes: 13 additions & 0 deletions bundles/org.openhab.io.yamlcomposer/NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.

* Project home: https://www.openhab.org

== Declared Project Licenses

This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.

== Source Code

https://github.com/openhab/openhab-addons
329 changes: 329 additions & 0 deletions bundles/org.openhab.io.yamlcomposer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,329 @@
# YAML Composer

YAML Composer introduces extended YAML features that make openHAB configuration more modular, reusable, and maintainable. These features let you structure configuration as composable building blocks rather than large, repetitive files.

The add-on loads enhanced-syntax YAML files from `OPENHAB_CONF/yamlcomposer/` and compiles them into fully resolved plain YAML written to `OPENHAB_CONF/yaml/composed/`.

[[toc]]

## Feature Summary

YAML Composer adds several enhancements on top of standard YAML.
Each feature addresses a different kind of reuse, composition, or abstraction to help you build cleaner and more maintainable YAML.

| Feature | Purpose | Typical Use |
|--------------------------------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|
| **Variables and Substitution (`${..}`)** | Insert dynamic values or evaluate expressions | Build labels, topics, IDs, or computed values |
| **Conditionals (`!if`)** | Conditionally include or exclude YAML blocks | Enable or disable features when using packages or template flags |
| **Include (`!include`)** | Insert the contents of another file | Reuse YAML across files; parameterize reusable blocks |
| **Templates (`!insert`)** | Reuse YAML defined within the same file | Local parameterized blocks; reusable channel or item fragments |
| **Packages** | Bundle multiple top-level sections into one reusable unit | Define reusable device structures containing things, items, metadata; sourced from external files or templates |
| **Anchors and Aliases (`&name`, `*name`)** | Define small, reusable YAML fragments | Static defaults, shared fields |
| **Merge Keys (`<<:`)** | Combine mappings from multiple sources | Layer defaults, override fields, compose structures |

Each feature has a dedicated documentation page:

- [Variables and Substitution](doc/variables.md)
- [Conditionals](doc/conditionals.md)
- [Include](doc/include.md)
- [Templates](doc/templates.md)
- [Packages](doc/packages.md)
- [Anchors and Aliases](doc/anchors.md)
- [Merge Keys](doc/merge-keys.md)

These features can be used independently, but they become especially powerful when combined.

For a general introduction to YAML, see [YAML Basics](doc/basics.md).

## Packaging Example

**CONF/yamlcomposer/LivingRoom.yaml:**

```yaml
version: 1

variables:
# Captures "LivingRoom" from the filename automatically
location: ${__FILE_NAME__} # => LivingRoom

packages:
Light1: !include
file: $pkg/zigbee_light.inc.yaml
vars: &LIGHT_VARS
color_temperature: {} # Include color temperature feature
power: # Customize the power item
groups:
- gInsideLights
- gSmartLights

Light2: !include
file: $pkg/zigbee_light.inc.yaml
vars:
<<: *LIGHT_VARS
```

**CONF/yamlcomposer/pkg/zigbee_light.inc.yaml:**

Comment thread
jimtng marked this conversation as resolved.
```yaml
# This is the package file, i.e. the template for a zigbee light
variables:
id: ${location}_${package_id}
thingid: ${id | lower | replace("_", "-")}
equipment: ${id}_Equipment
label: ${id | label}

# Set defaults
power: &DEFAULTS
groups: []
dimmer:
<<: *DEFAULTS

things:
mqtt:topic:${thingid}:
bridge: mqtt:broker:mosquitto
channels:
power:
type: switch
config:
stateTopic: zigbee2mqtt/${thingid}/state
commandTopic: zigbee2mqtt/${thingid}/set/state

dimmer:
type: dimmer
config:
stateTopic: zigbee2mqtt/${thingid}/brightness
commandTopic: zigbee2mqtt/${thingid}/set/brightness

<<: !if
if: VARS.containsKey('color_temperature')
then:
color-temperature: !sub
type: dimmer
config:
stateTopic: zigbee2mqtt/${thingid}/color_temp
commandTopic: zigbee2mqtt/${thingid}/set/color_temp

items:
${equipment}:
type: Group
label: ${label} Equipment
tags: [Lightbulb]
groups: ${[location]}

${id}:
type: Switch
label: ${label}
tags: [Control, Light]
groups: ${[equipment] + power.groups}
channel: mqtt:topic:${thingid}:power

${id}_Dimmer:
type: Dimmer
label: ${label} Brightness
tags: [Control, Level]
groups: ${[equipment] + dimmer.groups}
channel: mqtt:topic:${thingid}:dimmer

<<: !if
if: VARS.containsKey('color_temperature')
then:
${id}_CT:
type: Dimmer
label: ${label} Color Temperature
tags: [Control, ColorTemperature]
groups: ${[equipment] + color_temperature.groups}
channel: mqtt:topic:${thingid}:color-temperature
```

<details>
<summary><b>Output in CONF/yaml/composed/LivingRoom.yaml:</b></summary>

```yaml
version: 1

things:
mqtt:topic:livingroom-light1:
bridge: mqtt:broker:mosquitto
channels:
power:
type: switch
config:
stateTopic: zigbee2mqtt/livingroom-light1/state
commandTopic: zigbee2mqtt/livingroom-light1/set/state
dimmer:
type: dimmer
config:
stateTopic: zigbee2mqtt/livingroom-light1/brightness
commandTopic: zigbee2mqtt/livingroom-light1/set/brightness
color-temperature:
type: dimmer
config:
stateTopic: zigbee2mqtt/livingroom-light1/color_temp
commandTopic: zigbee2mqtt/livingroom-light1/set/color_temp

mqtt:topic:livingroom-light2:
bridge: mqtt:broker:mosquitto
channels:
power:
type: switch
config:
stateTopic: zigbee2mqtt/livingroom-light2/state
commandTopic: zigbee2mqtt/livingroom-light2/set/state
dimmer:
type: dimmer
config:
stateTopic: zigbee2mqtt/livingroom-light2/brightness
commandTopic: zigbee2mqtt/livingroom-light2/set/brightness
color-temperature:
type: dimmer
config:
stateTopic: zigbee2mqtt/livingroom-light2/color_temp
commandTopic: zigbee2mqtt/livingroom-light2/set/color_temp

items:
LivingRoom_Light1_Equipment:
type: Group
label: Living Room Light 1 Equipment
tags:
- Lightbulb
groups:
- LivingRoom

LivingRoom_Light1:
type: Switch
label: Living Room Light 1
tags:
- Control
- Light
groups:
- LivingRoom_Light1_Equipment
- gInsideLights
- gSmartLights
channel: mqtt:topic:livingroom-light1:power

LivingRoom_Light1_Dimmer:
type: Dimmer
label: Living Room Light 1 Brightness
tags:
- Control
- Level
groups:
- LivingRoom_Light1_Equipment
channel: mqtt:topic:livingroom-light1:dimmer

LivingRoom_Light1_CT:
type: Dimmer
label: Living Room Light 1 Color Temperature
tags:
- Control
- ColorTemperature
groups:
- LivingRoom_Light1_Equipment
channel: mqtt:topic:livingroom-light1:color-temperature

LivingRoom_Light2_Equipment:
type: Group
label: Living Room Light 2 Equipment
tags:
- Lightbulb
groups:
- LivingRoom

LivingRoom_Light2:
type: Switch
label: Living Room Light 2
tags:
- Control
- Light
groups:
- LivingRoom_Light2_Equipment
- gInsideLights
- gSmartLights
channel: mqtt:topic:livingroom-light2:power

LivingRoom_Light2_Dimmer:
type: Dimmer
label: Living Room Light 2 Brightness
tags:
- Control
- Level
groups:
- LivingRoom_Light2_Equipment
channel: mqtt:topic:livingroom-light2:dimmer

LivingRoom_Light2_CT:
type: Dimmer
label: Living Room Light 2 Color Temperature
tags:
- Control
- ColorTemperature
groups:
- LivingRoom_Light2_Equipment
channel: mqtt:topic:livingroom-light2:color-temperature
```

</details>

## Processing Overview

YAML Composer reads enhanced-syntax YAML files and performs a compilation pass that expands all extended features into a single, fully resolved YAML document that openHAB can load.

During compilation, YAML Composer performs the following steps:

1. **YAML Parsing**: The source file is parsed into an internal structure.
1. **Variable Substitution (`${..}`)**: Expressions are evaluated and injected.
1. **Conditionals (`!if`)**: Conditional logic determines which blocks remain.
1. **Template and Include Expansion**: `!insert` and `!include` bring in referenced content, often using resolved variables.
1. **Package Expansion**: External or local packages are loaded and expanded into their component sections.
1. **Recursive Merging**: Merge keys and package structures are combined into the main document.
1. **Hidden Key Removal**: Keys beginning with `.` are removed from the final output.

The resulting YAML contains:

- all variables resolved
- all conditionals evaluated
- all templates and includes expanded
- all anchors and merges applied
- all packages integrated
- all hidden keys removed

This final compiled YAML is written to `OPENHAB_CONF/yaml/composed/`, where openHAB loads it as Things, Items, Metadata, and other configuration elements as defined by the Core YAML Configuration structure.

## Hidden Keys

Keys beginning with a dot (`.`) are treated as hidden. They:

- exist only during compilation
- are ideal for storing anchors, templates, or shared structures
- keep visible configuration clean
- are removed from the final output

**Example:**

```yaml
.base-switch: &BASE_SWITCH
type: Switch
autoupdate: false

items:
Light1:
<<: *BASE_SWITCH
label: Light One
```

## File Structure and Conventions

YAML files can be organized freely, but the following conventions improve clarity and maintainability:

- Place `variables:` and `templates:` near the top of the file.
- Group reusable structures under hidden keys.
- Use anchors for static fragments and includes for parameterized ones.
- Keep packages in separate files when they represent reusable device or feature definitions.

These conventions are optional but help keep complex configurations predictable and easy to navigate.

### File naming convention

- Use `*.yaml` or `*.yml` for main source files that produce composed output.
- Use `*.inc.yaml` or `*.inc.yml` for include fragments referenced by `!include`.
- Mixed-role files (both main and include) are not supported.
Loading
Loading