|
| 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