-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
[yamlcomposer] YAML Composer Add-on for Enhanced YAML Preprocessing #20305
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
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 fe1482a
improve watch logic around deletion and load time
jimtng 1f4a8a4
address copilot review
jimtng 3ad94cb
fix markdownlint issues
jimtng 130106f
adjust parseMapParameters to pass test
jimtng b7f1c93
update doc on package list merge mechanism
jimtng 245fd1e
remove unused MergeKeyProcessor
jimtng 4b0ade0
defend against null return from TemplateError.getMessage()
jimtng 0dab0c1
address copilot review
jimtng 3653ed0
add dependencies in features.xml, fix add-on spelling
jimtng 2aa6e96
update itest.bndrun
jimtng e558edb
add snakeyaml-engine in features.xml
jimtng c981f9d
fix javadoc in IncludeProcessor
jimtng 7efe085
Clarify the documentation of nested packages, !remove and !replace ta…
jimtng 5bbeb0c
wrap single line ifs with braces
jimtng 2bae740
add snakeyaml-engine to itest.bndrun
jimtng e07c85b
improve include logging
jimtng 21ac9ea
fix javadoc for RecursiveTransformer#register()
jimtng 16ec359
add support for merge keys at the top-level of the templates section
jimtng 1f1f2b4
document limitation of merge keys in packages
jimtng 36c3192
Prevent !replace/!remove from being processed in the template inserti…
jimtng a4ab3f3
Add note that using the same file as main file and as include is not …
jimtng 71dc5ef
warn on null package keys
jimtng 280648c
narrow down bnd.importpackage
jimtng da5183e
minor clean up caught by copilot
jimtng 3e750fd
apply copilot suggestions.
jimtng daade99
only write readme.txt when we had to create the composed dir
jimtng 51a03e1
spotless
jimtng a1be4ed
change source/output root for clean up
jimtng File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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:** | ||
|
|
||
| ```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. | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.