You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[yamlcomposer] Add !for loops, scoped local variables (!var), and enhance conditionals (!elseif, !else) (#21408)
* [yamlcomposer] Add control flow directives (`!if`, `!else`, `!for`), local variables (`!var`), `range()` function, and `enumerate` support
Signed-off-by: Jimmy Tanagra <jcode@tanagra.id.au>
|**Variables and Substitution (`${..}`)**| Insert dynamic values or evaluate expressions | Build labels, topics, IDs, or computed values |
29
30
|**Conditionals (`!if`)**| Conditionally include or exclude YAML blocks | Enable or disable features when using packages or template flags |
31
+
|**Loops (`!for`)**| Generate repeated YAML blocks from a list or map | Create multiple items, channels, or thing definitions from structured data |
30
32
|**Include (`!include`)**| Insert the contents of another file | Reuse YAML across files; parameterize reusable blocks |
31
33
|**Templates (`!insert`)**| Reuse YAML defined within the same file | Local parameterized blocks; reusable channel or item fragments |
32
34
|**Packages**| Bundle multiple top-level sections into one reusable unit | Define reusable device structures containing things, items, metadata; sourced from external files or templates |
@@ -37,6 +39,7 @@ Each feature has a dedicated documentation page:
The `!if` tag performs logical branching during the **preprocessing phase**.
3
+
Conditional tags perform logical branching during the **preprocessing phase**.
4
4
5
5
> **Note:** Conditions are evaluated **once** when the YAML file is loaded.
6
6
> These are not runtime rules.
7
7
> They do not react to live state changes in openHAB.
8
8
9
9
[[toc]]
10
10
11
-
## When to Use `!if`
11
+
## When to Use Conditional Tags
12
12
13
-
Use the `!if` tag to adapt your configuration based on the **Resolution Context** (variables defined in the file, injected via `!include` or `!insert`, or [environment globals](variables.md#env-to-access-environment-variables)).
13
+
Use conditional tags to adapt your configuration based on the **Resolution Context**.
14
+
The Resolution Context includes variables defined in the file, variables injected via `!include` or `!insert`, and [environment globals](variables.md#env-to-access-environment-variables).
15
+
16
+
Conditional tags are useful for selecting configuration blocks, enabling optional features, or merging additional properties.
14
17
15
18
-**Conditional Snippets**: choose between alternative configuration blocks or values.
16
19
-**Optional Properties**: conditionally merge in additional settings using [merge keys (<<)](merge-keys.md).
20
+
-**Multi-Branch Logic**: select one of several possible configuration branches.
21
+
22
+
## Conditional Forms
23
+
24
+
The conditional system supports three forms:
25
+
26
+
-**Key‑Level Form**: uses `!if`, `!elseif` (and aliases `!elsif`, `!elif`), and `!else ~:` as map keys.
27
+
-**Mapping Form**: uses `if:`, `then:`, and `else:` keys inside a single mapping.
28
+
-**Sequence Form**: uses `if:`, `elseif:`, and `else:` entries inside a list.
29
+
30
+
All forms evaluate expressions once during preprocessing.
31
+
32
+
### Differences Between the Three Forms
33
+
34
+
Each form serves a different purpose and behaves differently when used inside maps or lists.
35
+
The table below summarizes the key differences so you can choose the right form before diving into the detailed syntax.
|**Behavior in Maps**| Merges nested key‑value pairs directly into the parent map | Resolves to a single value | Resolves to a single value |
41
+
|**Behavior in Lists**| Splices items directly into the parent list | Returns a single list element (branch may itself be a list) | Returns a single list element (branch may itself be a list) |
|**Unmatched Fallback**| Inactive branches are omitted entirely | Resolves to `null`| Resolves to `null`|
44
+
|**Primary Use Case**| Conditionally merging groups of properties or inserting multiple inline list items | Simple ternary scalar/container assignment | Multi‑branch ternary scalar/container assignment |
45
+
46
+
## Key‑Level Form
47
+
48
+
The key‑level form applies conditional tags directly as map keys.
49
+
The tags `!if`, `!elseif` (and its aliases `!elsif`, `!elif`), and `!else ~` allow multi‑branch logic using separate map entries.
50
+
Each tag evaluates its expression (except `!else ~`, which has no expression).
51
+
The nested content (map or list) underneath the selected tag is merged into the parent structure.
52
+
53
+
Use this form when you want to conditionally merge additional map entries or list items into the surrounding structure.
54
+
55
+
::: tip Hints
56
+
57
+
- Expressions may be quoted or unquoted.
58
+
Quote expressions when they contain characters YAML might misinterpret, such as `:` or `#`.
59
+
- If the expression is truthy, the nested content (map or list) is merged into the parent structure.
60
+
- If the expression is falsy, the nested content is ignored.
61
+
- Branches are evaluated in order from top to bottom.
62
+
Only the first truthy branch is selected.
63
+
Inactive branches are ignored.
64
+
65
+
:::
17
66
18
-
##Basic Syntax
67
+
### Simple Example
19
68
20
-
The `!if` tag supports two forms: a **Mapping Form** for simple logic and a **Sequence Form** for multiple branches.
69
+
```yaml
70
+
variables:
71
+
items_count: 20
72
+
things_count: 5
73
+
74
+
test:
75
+
!if '"bar" == "bar"':
76
+
foo: bar
77
+
!if items_count > 10:
78
+
items: a lot of items
79
+
!if things_count > 10:
80
+
things: a lot of things
81
+
other: baz
82
+
```
21
83
22
-
The `if:` and `elseif:` keys are treated as **implicit expressions**.
23
-
You do not need to wrap the expression in `${...}`.
84
+
Result:
24
85
25
-
### Mapping Form (Simple)
86
+
```yaml
87
+
test:
88
+
foo: bar
89
+
items: a lot of items
90
+
other: baz
91
+
```
26
92
27
-
Use this for simple if/else decisions.
93
+
### List Example
94
+
95
+
```yaml
96
+
variables:
97
+
add_extra: true
98
+
99
+
items:
100
+
MyItem:
101
+
tags:
102
+
- alpha
103
+
- beta
104
+
- !ifadd_extra:
105
+
- gamma
106
+
- delta
107
+
- epsilon
108
+
```
109
+
110
+
Result:
111
+
112
+
```yaml
113
+
items:
114
+
MyItem:
115
+
tags:
116
+
- alpha
117
+
- beta
118
+
- gamma
119
+
- delta
120
+
- epsilon
121
+
```
122
+
123
+
### Multi‑Branch Example
124
+
125
+
```yaml
126
+
mode:
127
+
!if "env == 'prod'":
128
+
value: "production"
129
+
!elseif "env == 'staging'":
130
+
value: "staging"
131
+
!elif "env == 'dev'": # !elseif, !elif, and !elsif can be used interchangeably
132
+
value: "development"
133
+
!else ~:
134
+
value: "unknown"
135
+
```
136
+
137
+
Result:
138
+
139
+
```yaml
140
+
mode:
141
+
value: "production"
142
+
```
143
+
144
+
::: tip Important — `!else` Requires `~`
145
+
146
+
A bare `!else:` is **invalid YAML** because it produces an empty mapping.
147
+
148
+
You **must** write:
149
+
150
+
```yaml
151
+
!else ~:
152
+
```
153
+
154
+
The `~` is YAML's canonical `null` literal and exists only to satisfy YAML's requirement that every key has a value.
155
+
156
+
:::
157
+
158
+
### Key Uniqueness
159
+
160
+
Each conditional tag is a map key.
161
+
YAML requires each key to be unique.
162
+
A duplicate‑key error occurs only when two conditional tags have identical key scalars.
163
+
Inline comments may be used to make identical expressions unique.
164
+
165
+
```yaml
166
+
test:
167
+
!if "true # unique 1":
168
+
foo: bar
169
+
!if "true # unique 2":
170
+
qux: quux
171
+
!if "true # unique 3":
172
+
corge: grault
173
+
```
174
+
175
+
### Returning Simple Values
176
+
177
+
The _key‑level form_ is intended for returning maps and lists.
178
+
Use inline expressions when you want to return a simple scalar value.
179
+
Inline expressions do not require conditional tags.
180
+
Inline expressions evaluate directly to a single value.
181
+
182
+
```yaml
183
+
foo: ${"High" if 5 > 2 else "Low"}
184
+
```
185
+
186
+
Alternatively the other two forms below also return a single value.
187
+
188
+
## Mapping Form
189
+
190
+
Use the mapping form for simple if/else decisions inside a single block.
0 commit comments