Skip to content

Commit 0a26a04

Browse files
authored
[yamlcomposer] YAML Composer Add-on for Enhanced YAML Preprocessing (openhab#20305)
* [yamlcomposer] New Yaml Composer add-on Signed-off-by: Jimmy Tanagra <jcode@tanagra.id.au>
1 parent 67394e2 commit 0a26a04

64 files changed

Lines changed: 10262 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,7 @@
473473
/bundles/org.openhab.io.metrics/ @pravussum
474474
/bundles/org.openhab.io.neeo/ @morph166955
475475
/bundles/org.openhab.io.openhabcloud/ @kaikreuzer
476+
/bundles/org.openhab.io.yamlcomposer/ @jimtng
476477
/bundles/org.openhab.persistence.dynamodb/ @ssalonen
477478
/bundles/org.openhab.persistence.influxdb/ @lujop
478479
/bundles/org.openhab.persistence.inmemory/ @J-N-K
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
This content is produced and maintained by the openHAB project.
2+
3+
* Project home: https://www.openhab.org
4+
5+
== Declared Project Licenses
6+
7+
This program and the accompanying materials are made available under the terms
8+
of the Eclipse Public License 2.0 which is available at
9+
https://www.eclipse.org/legal/epl-2.0/.
10+
11+
== Source Code
12+
13+
https://github.com/openhab/openhab-addons
Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
# YAML Composer
2+
3+
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.
4+
5+
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/`.
6+
7+
[[toc]]
8+
9+
## Feature Summary
10+
11+
YAML Composer adds several enhancements on top of standard YAML.
12+
Each feature addresses a different kind of reuse, composition, or abstraction to help you build cleaner and more maintainable YAML.
13+
14+
| Feature | Purpose | Typical Use |
15+
|--------------------------------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|
16+
| **Variables and Substitution (`${..}`)** | Insert dynamic values or evaluate expressions | Build labels, topics, IDs, or computed values |
17+
| **Conditionals (`!if`)** | Conditionally include or exclude YAML blocks | Enable or disable features when using packages or template flags |
18+
| **Include (`!include`)** | Insert the contents of another file | Reuse YAML across files; parameterize reusable blocks |
19+
| **Templates (`!insert`)** | Reuse YAML defined within the same file | Local parameterized blocks; reusable channel or item fragments |
20+
| **Packages** | Bundle multiple top-level sections into one reusable unit | Define reusable device structures containing things, items, metadata; sourced from external files or templates |
21+
| **Anchors and Aliases (`&name`, `*name`)** | Define small, reusable YAML fragments | Static defaults, shared fields |
22+
| **Merge Keys (`<<:`)** | Combine mappings from multiple sources | Layer defaults, override fields, compose structures |
23+
24+
Each feature has a dedicated documentation page:
25+
26+
- [Variables and Substitution](doc/variables.md)
27+
- [Conditionals](doc/conditionals.md)
28+
- [Include](doc/include.md)
29+
- [Templates](doc/templates.md)
30+
- [Packages](doc/packages.md)
31+
- [Anchors and Aliases](doc/anchors.md)
32+
- [Merge Keys](doc/merge-keys.md)
33+
34+
These features can be used independently, but they become especially powerful when combined.
35+
36+
For a general introduction to YAML, see [YAML Basics](doc/basics.md).
37+
38+
## Packaging Example
39+
40+
**CONF/yamlcomposer/LivingRoom.yaml:**
41+
42+
```yaml
43+
version: 1
44+
45+
variables:
46+
# Captures "LivingRoom" from the filename automatically
47+
location: ${__FILE_NAME__} # => LivingRoom
48+
49+
packages:
50+
Light1: !include
51+
file: $pkg/zigbee_light.inc.yaml
52+
vars: &LIGHT_VARS
53+
color_temperature: {} # Include color temperature feature
54+
power: # Customize the power item
55+
groups:
56+
- gInsideLights
57+
- gSmartLights
58+
59+
Light2: !include
60+
file: $pkg/zigbee_light.inc.yaml
61+
vars:
62+
<<: *LIGHT_VARS
63+
```
64+
65+
**CONF/yamlcomposer/pkg/zigbee_light.inc.yaml:**
66+
67+
```yaml
68+
# This is the package file, i.e. the template for a zigbee light
69+
variables:
70+
id: ${location}_${package_id}
71+
thingid: ${id | lower | replace("_", "-")}
72+
equipment: ${id}_Equipment
73+
label: ${id | label}
74+
75+
# Set defaults
76+
power: &DEFAULTS
77+
groups: []
78+
dimmer:
79+
<<: *DEFAULTS
80+
81+
things:
82+
mqtt:topic:${thingid}:
83+
bridge: mqtt:broker:mosquitto
84+
channels:
85+
power:
86+
type: switch
87+
config:
88+
stateTopic: zigbee2mqtt/${thingid}/state
89+
commandTopic: zigbee2mqtt/${thingid}/set/state
90+
91+
dimmer:
92+
type: dimmer
93+
config:
94+
stateTopic: zigbee2mqtt/${thingid}/brightness
95+
commandTopic: zigbee2mqtt/${thingid}/set/brightness
96+
97+
<<: !if
98+
if: VARS.containsKey('color_temperature')
99+
then:
100+
color-temperature: !sub
101+
type: dimmer
102+
config:
103+
stateTopic: zigbee2mqtt/${thingid}/color_temp
104+
commandTopic: zigbee2mqtt/${thingid}/set/color_temp
105+
106+
items:
107+
${equipment}:
108+
type: Group
109+
label: ${label} Equipment
110+
tags: [Lightbulb]
111+
groups: ${[location]}
112+
113+
${id}:
114+
type: Switch
115+
label: ${label}
116+
tags: [Control, Light]
117+
groups: ${[equipment] + power.groups}
118+
channel: mqtt:topic:${thingid}:power
119+
120+
${id}_Dimmer:
121+
type: Dimmer
122+
label: ${label} Brightness
123+
tags: [Control, Level]
124+
groups: ${[equipment] + dimmer.groups}
125+
channel: mqtt:topic:${thingid}:dimmer
126+
127+
<<: !if
128+
if: VARS.containsKey('color_temperature')
129+
then:
130+
${id}_CT:
131+
type: Dimmer
132+
label: ${label} Color Temperature
133+
tags: [Control, ColorTemperature]
134+
groups: ${[equipment] + color_temperature.groups}
135+
channel: mqtt:topic:${thingid}:color-temperature
136+
```
137+
138+
<details>
139+
<summary><b>Output in CONF/yaml/composed/LivingRoom.yaml:</b></summary>
140+
141+
```yaml
142+
version: 1
143+
144+
things:
145+
mqtt:topic:livingroom-light1:
146+
bridge: mqtt:broker:mosquitto
147+
channels:
148+
power:
149+
type: switch
150+
config:
151+
stateTopic: zigbee2mqtt/livingroom-light1/state
152+
commandTopic: zigbee2mqtt/livingroom-light1/set/state
153+
dimmer:
154+
type: dimmer
155+
config:
156+
stateTopic: zigbee2mqtt/livingroom-light1/brightness
157+
commandTopic: zigbee2mqtt/livingroom-light1/set/brightness
158+
color-temperature:
159+
type: dimmer
160+
config:
161+
stateTopic: zigbee2mqtt/livingroom-light1/color_temp
162+
commandTopic: zigbee2mqtt/livingroom-light1/set/color_temp
163+
164+
mqtt:topic:livingroom-light2:
165+
bridge: mqtt:broker:mosquitto
166+
channels:
167+
power:
168+
type: switch
169+
config:
170+
stateTopic: zigbee2mqtt/livingroom-light2/state
171+
commandTopic: zigbee2mqtt/livingroom-light2/set/state
172+
dimmer:
173+
type: dimmer
174+
config:
175+
stateTopic: zigbee2mqtt/livingroom-light2/brightness
176+
commandTopic: zigbee2mqtt/livingroom-light2/set/brightness
177+
color-temperature:
178+
type: dimmer
179+
config:
180+
stateTopic: zigbee2mqtt/livingroom-light2/color_temp
181+
commandTopic: zigbee2mqtt/livingroom-light2/set/color_temp
182+
183+
items:
184+
LivingRoom_Light1_Equipment:
185+
type: Group
186+
label: Living Room Light 1 Equipment
187+
tags:
188+
- Lightbulb
189+
groups:
190+
- LivingRoom
191+
192+
LivingRoom_Light1:
193+
type: Switch
194+
label: Living Room Light 1
195+
tags:
196+
- Control
197+
- Light
198+
groups:
199+
- LivingRoom_Light1_Equipment
200+
- gInsideLights
201+
- gSmartLights
202+
channel: mqtt:topic:livingroom-light1:power
203+
204+
LivingRoom_Light1_Dimmer:
205+
type: Dimmer
206+
label: Living Room Light 1 Brightness
207+
tags:
208+
- Control
209+
- Level
210+
groups:
211+
- LivingRoom_Light1_Equipment
212+
channel: mqtt:topic:livingroom-light1:dimmer
213+
214+
LivingRoom_Light1_CT:
215+
type: Dimmer
216+
label: Living Room Light 1 Color Temperature
217+
tags:
218+
- Control
219+
- ColorTemperature
220+
groups:
221+
- LivingRoom_Light1_Equipment
222+
channel: mqtt:topic:livingroom-light1:color-temperature
223+
224+
LivingRoom_Light2_Equipment:
225+
type: Group
226+
label: Living Room Light 2 Equipment
227+
tags:
228+
- Lightbulb
229+
groups:
230+
- LivingRoom
231+
232+
LivingRoom_Light2:
233+
type: Switch
234+
label: Living Room Light 2
235+
tags:
236+
- Control
237+
- Light
238+
groups:
239+
- LivingRoom_Light2_Equipment
240+
- gInsideLights
241+
- gSmartLights
242+
channel: mqtt:topic:livingroom-light2:power
243+
244+
LivingRoom_Light2_Dimmer:
245+
type: Dimmer
246+
label: Living Room Light 2 Brightness
247+
tags:
248+
- Control
249+
- Level
250+
groups:
251+
- LivingRoom_Light2_Equipment
252+
channel: mqtt:topic:livingroom-light2:dimmer
253+
254+
LivingRoom_Light2_CT:
255+
type: Dimmer
256+
label: Living Room Light 2 Color Temperature
257+
tags:
258+
- Control
259+
- ColorTemperature
260+
groups:
261+
- LivingRoom_Light2_Equipment
262+
channel: mqtt:topic:livingroom-light2:color-temperature
263+
```
264+
265+
</details>
266+
267+
## Processing Overview
268+
269+
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.
270+
271+
During compilation, YAML Composer performs the following steps:
272+
273+
1. **YAML Parsing**: The source file is parsed into an internal structure.
274+
1. **Variable Substitution (`${..}`)**: Expressions are evaluated and injected.
275+
1. **Conditionals (`!if`)**: Conditional logic determines which blocks remain.
276+
1. **Template and Include Expansion**: `!insert` and `!include` bring in referenced content, often using resolved variables.
277+
1. **Package Expansion**: External or local packages are loaded and expanded into their component sections.
278+
1. **Recursive Merging**: Merge keys and package structures are combined into the main document.
279+
1. **Hidden Key Removal**: Keys beginning with `.` are removed from the final output.
280+
281+
The resulting YAML contains:
282+
283+
- all variables resolved
284+
- all conditionals evaluated
285+
- all templates and includes expanded
286+
- all anchors and merges applied
287+
- all packages integrated
288+
- all hidden keys removed
289+
290+
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.
291+
292+
## Hidden Keys
293+
294+
Keys beginning with a dot (`.`) are treated as hidden. They:
295+
296+
- exist only during compilation
297+
- are ideal for storing anchors, templates, or shared structures
298+
- keep visible configuration clean
299+
- are removed from the final output
300+
301+
**Example:**
302+
303+
```yaml
304+
.base-switch: &BASE_SWITCH
305+
type: Switch
306+
autoupdate: false
307+
308+
items:
309+
Light1:
310+
<<: *BASE_SWITCH
311+
label: Light One
312+
```
313+
314+
## File Structure and Conventions
315+
316+
YAML files can be organized freely, but the following conventions improve clarity and maintainability:
317+
318+
- Place `variables:` and `templates:` near the top of the file.
319+
- Group reusable structures under hidden keys.
320+
- Use anchors for static fragments and includes for parameterized ones.
321+
- Keep packages in separate files when they represent reusable device or feature definitions.
322+
323+
These conventions are optional but help keep complex configurations predictable and easy to navigate.
324+
325+
### File naming convention
326+
327+
- Use `*.yaml` or `*.yml` for main source files that produce composed output.
328+
- Use `*.inc.yaml` or `*.inc.yml` for include fragments referenced by `!include`.
329+
- Mixed-role files (both main and include) are not supported.

0 commit comments

Comments
 (0)