YAML Composer Tips

The YAML Composer features a template system that drastically reduces repetition in your configuration files. Instead of copying and pasting code, you can write a single template to define multiple similar devices.

I’m going to post some examples of how I’m using it in my installation.

This is an ongoing process so I’ll be posting more as I go.

I’ve been asking Gemini to help me tidy up the syntaxes here. It produced some minor syntax errors, but after I corrected them, Gemini then produced the correct syntax.

The result is pretty amazing I think. My initial version was very messy and unstructured. Gemini helped me come up with a clean structure.

Visual Studio Code Settings

In the vscode settings, add this so it doesn’t show the error wiggly lines on our custom tags:

{
  "yaml.customTags": [
      "!for",
      "!if",
      "!if mapping",
      "!var",
      "!var mapping",
      "!include",
      "!include mapping",
      "!insert",
      "!insert mapping",
      "!elseif",
      "!elsif",
      "!elif",
      "!else",
      "!literal",
      "!literal mapping",
      "!sub",
      "!sub mapping",
      "!deep",
      "!remove",
      "!replace",
      "!replace mapping",
      "!default",
      "!default mapping",
      "!freeze",
      "!freeze mapping",
    ],
}

Defining My Semantic Locations

This first example demonstrates the for loop ability coupled with template recursion.
(I asked Gemini to come up with / generate the recursion scheme for me)

It enables me to edit and define my locations in a very simple hierarchy that’s easy to instantly see and understand.

Note: !for loops were just merged into 5.3 snapshot. Loops are not yet supported in 5.2.
Template, variables, aliases/anchors are available in 5.2.

version: 1

variables:
  # Hierarchical location structure for the home, with indoor and outdoor locations
  locations:
    Indoor:
      FrontEntry:
      Hallway:
      LivingRoom:
      Kitchen:
      LoungeRoom:
      LaundryRoom:
      Garage:
      BedRooms:
        MasterBedRoom:
          MasterBathRoom:
          MasterToilet:
        BedRoom1:
        BedRoom2:
      StudyRoom:
      StudioRoom:
      MainBathRoom:
      MainToilet:
    Outdoor:
      Driveway:
      FrontPorch:
      FrontYard:
      BackYard:
      BackPorch:
      ClothesLine:
      SwimmingPool:

  # Centralized location properties lookup table
  location_config:
    # Indoor Locations
    Indoor: {tags: [Indoor], icon: "if:lucide:home"}
    FrontEntry: {tags: [Entry], icon: "if:lucide:door-open"}
    Hallway: {tags: [Corridor], icon: "if:material-symbols:hallway-outline"}
    LivingRoom: {tags: [FamilyRoom], icon: "if:lucide:sofa"}
    Kitchen: {tags: [Kitchen], icon: "if:lucide:cooking-pot"}
    LoungeRoom: {tags: [FamilyRoom], icon: "if:lucide:tv"}
    LaundryRoom: {tags: [LaundryRoom], icon: "if:lucide:washing-machine"}
    Garage: {tags: [Garage], icon: "if:lucide:car"}
    BedRooms: {tags: [Bedroom], icon: "if:lucide:bed-double"}
    MasterBedRoom: {tags: [Bedroom], icon: "if:lucide:bed-double"}
    MasterBathRoom: {tags: [Bathroom], icon: "if:lucide:bath"}
    MasterToilet: {tags: [Bathroom, Toilet], icon: "if:material-symbols:wc"}
    BedRoom1: {tags: [Bedroom], icon: "if:lucide:bed"}
    BedRoom2: {tags: [Bedroom], icon: "if:lucide:bed"}
    StudyRoom: {tags: [Room], icon: "if:lucide:laptop"}
    StudioRoom: {tags: [Room], icon: "if:lucide:palette"}
    MainBathRoom: {tags: [Bathroom], icon: "if:lucide:bath"}
    MainToilet: {tags: [Bathroom, Toilet], icon: "if:material-symbols:wc"}

    # Outdoor Locations (Includes preposition)
    Outdoor: {tags: [Outdoor], icon: "if:lucide:trees", prep: "in"}
    Driveway: {tags: [Carport], icon: "if:material-symbols:garage-door-outline", prep: "on"}
    FrontPorch: {tags: [Porch], icon: "if:material-symbols:deck-outline", prep: "on"}
    FrontYard: {tags: [Garden], icon: "if:lucide:flower-2", prep: "in"}
    BackYard: {tags: [Garden], icon: "if:lucide:sun", prep: "in"}
    BackPorch: {tags: [Porch], icon: "if:material-symbols:deck-outline", prep: "on"}
    ClothesLine: {tags: [Outdoor], icon: "if:lucide:shirt", prep: "near"}
    SwimmingPool: {tags: [SwimmingPool], icon: "if:material-symbols:pool-outline", prep: "at"}

templates:
  create_location_groups:
    !for name, children in node_map:
      !var conf: ${location_config[name] || {}}

      ${name}:
        type: Group
        label: "${name | label}"
        icon: ${conf.icon || "iconify:carbon:floorplan"}
        tags: ${conf.tags || [name]}

        !if parent_name:
          groups: ["${parent_name}"]

        !if conf.prep:
          metadata:
            preposition: ${conf.prep}

      # Recurse into children if the map value contains child entries
      !if children && !children.isEmpty: !insert
        template: create_location_groups
        vars:
          node_map: ${children}
          parent_name: ${name}

items: !insert
  template: create_location_groups
  vars:
    node_map: ${locations}
    parent_name: null
Output
# ==============================================================================
# Generated by openHAB 5.3.0 (Build #5555) YAML Composer, DO NOT EDIT
# Source:    yamlcomposer/semantic_locations.yaml
# Generated: 2026-08-24T18:39:44.226776352+10:00[Australia/Brisbane]
# ==============================================================================

version: 1

items:
  Indoor:
    type: Group
    label: Indoor
    icon: if:lucide:home
    tags:
      - Indoor

  FrontEntry:
    type: Group
    label: Front Entry
    icon: if:lucide:door-open
    tags:
      - Entry
    groups:
      - Indoor

  Hallway:
    type: Group
    label: Hallway
    icon: if:material-symbols:hallway-outline
    tags:
      - Corridor
    groups:
      - Indoor

  LivingRoom:
    type: Group
    label: Living Room
    icon: if:lucide:sofa
    tags:
      - FamilyRoom
    groups:
      - Indoor

  Kitchen:
    type: Group
    label: Kitchen
    icon: if:lucide:cooking-pot
    tags:
      - Kitchen
    groups:
      - Indoor

  LoungeRoom:
    type: Group
    label: Lounge Room
    icon: if:lucide:tv
    tags:
      - FamilyRoom
    groups:
      - Indoor

  LaundryRoom:
    type: Group
    label: Laundry Room
    icon: if:lucide:washing-machine
    tags:
      - LaundryRoom
    groups:
      - Indoor

  Garage:
    type: Group
    label: Garage
    icon: if:lucide:car
    tags:
      - Garage
    groups:
      - Indoor

  BedRooms:
    type: Group
    label: Bed Rooms
    icon: if:lucide:bed-double
    tags:
      - Bedroom
    groups:
      - Indoor

  MasterBedRoom:
    type: Group
    label: Master Bed Room
    icon: if:lucide:bed-double
    tags:
      - Bedroom
    groups:
      - BedRooms

  MasterBathRoom:
    type: Group
    label: Master Bath Room
    icon: if:lucide:bath
    tags:
      - Bathroom
    groups:
      - MasterBedRoom

  MasterToilet:
    type: Group
    label: Master Toilet
    icon: if:material-symbols:wc
    tags:
      - Bathroom
      - Toilet
    groups:
      - MasterBedRoom

  BedRoom1:
    type: Group
    label: Bed Room 1
    icon: if:lucide:bed
    tags:
      - Bedroom
    groups:
      - BedRooms

  BedRoom2:
    type: Group
    label: Bed Room 2
    icon: if:lucide:bed
    tags:
      - Bedroom
    groups:
      - BedRooms

  StudyRoom:
    type: Group
    label: Study Room
    icon: if:lucide:laptop
    tags:
      - Room
    groups:
      - Indoor

  StudioRoom:
    type: Group
    label: Studio Room
    icon: if:lucide:palette
    tags:
      - Room
    groups:
      - Indoor

  MainBathRoom:
    type: Group
    label: Main Bath Room
    icon: if:lucide:bath
    tags:
      - Bathroom
    groups:
      - Indoor

  MainToilet:
    type: Group
    label: Main Toilet
    icon: if:material-symbols:wc
    tags:
      - Bathroom
      - Toilet
    groups:
      - Indoor

  Outdoor:
    type: Group
    label: Outdoor
    icon: if:lucide:trees
    tags:
      - Outdoor
    metadata:
      preposition: in

  Driveway:
    type: Group
    label: Driveway
    icon: if:material-symbols:garage-door-outline
    tags:
      - Carport
    groups:
      - Outdoor
    metadata:
      preposition: on

  FrontPorch:
    type: Group
    label: Front Porch
    icon: if:material-symbols:deck-outline
    tags:
      - Porch
    groups:
      - Outdoor
    metadata:
      preposition: on

  FrontYard:
    type: Group
    label: Front Yard
    icon: if:lucide:flower-2
    tags:
      - Garden
    groups:
      - Outdoor
    metadata:
      preposition: in

  BackYard:
    type: Group
    label: Back Yard
    icon: if:lucide:sun
    tags:
      - Garden
    groups:
      - Outdoor
    metadata:
      preposition: in

  BackPorch:
    type: Group
    label: Back Porch
    icon: if:material-symbols:deck-outline
    tags:
      - Porch
    groups:
      - Outdoor
    metadata:
      preposition: on

  ClothesLine:
    type: Group
    label: Clothes Line
    icon: if:lucide:shirt
    tags:
      - Outdoor
    groups:
      - Outdoor
    metadata:
      preposition: near

  SwimmingPool:
    type: Group
    label: Swimming Pool
    icon: if:material-symbols:pool-outline
    tags:
      - SwimmingPool
    groups:
      - Outdoor
    metadata:
      preposition: at

Defining General Groups

This demonstrates a nested for loop.

# Groups configuration for OpenHAB devices

version: 1

variables:
  groups:
    "Contact:OR:OPEN,CLOSED":
      gDoorsAndWindows:
      gExternalDoors: [gDoorsAndWindows]
      gWindows: [gDoorsAndWindows]
      gInternalDoors:
      gSecuritySensors:
      gLeftOpenAlert:

    "Switch:OR:ON,OFF":
      gSecurityLights:
      gLights:
      gInsideLights: [gLights]
      gOutsideLights: [gLights]
      gLivingRoomLights:
      gLoungeRoomLights:
      gMasterBedRoomLights:
      gBackYardLights:
        groups: [gOutsideLights]
        metadata:
          <<: !include "$inc/assistants.inc.yaml?tag=Light"
          synonyms: "Back Porch Lights"
      gBackPorch_Switch2:
      gMasterBedRoom_CeilingFan: [MasterBedRoom, gInsideLights]

    "Number:MAX":
      gIndoorHumidity: [gHumidity]

    default:
      gExhaustFans:
      gExhaustFanButtons:
      gLightButtons:
      gTemperature:
      gHumidity:
      gDimmers:
      gBrightness:
      gIPAddress:
      gPresenceSimulators:
      gGroupedSwitches:
      gDoorBell:

items:
  !for type_key, group_map in groups:
    !for name, raw_conf in group_map:
      # If raw_conf is a list, treat it as parent groups; otherwise use as-is
      !var conf: "${raw_conf is list ? { groups: raw_conf } : (raw_conf || {})}"

      "${name}":
        type: Group
        label: "${name.replaceFirst('^g(?=[A-Z])', '') | label}"

        !if type_key != 'default':
          !var type_parts: "${type_key.split(':')}"
          group:
            type: "${type_parts[0]}"
            function: "${type_parts[1]}"
            # split creates an array, not a list. Use `|length` instead of `.size`
            !if type_parts | length > 2:
              parameters: ${type_parts[2].split(',')}

        <<: ${conf}

Output
# ==============================================================================
# Generated by openHAB 5.3.0 (Build #5555) YAML Composer, DO NOT EDIT
# Source:    yamlcomposer/groups.yaml
# Generated: 2026-08-24T19:49:39.317181990+10:00[Australia/Brisbane]
# ==============================================================================

version: 1

items:
  gDoorsAndWindows:
    type: Group
    label: Doors And Windows
    group:
      type: Contact
      function: OR
      parameters:
        - OPEN
        - CLOSED

  gExternalDoors:
    type: Group
    label: External Doors
    group:
      type: Contact
      function: OR
      parameters:
        - OPEN
        - CLOSED

  gWindows:
    type: Group
    label: Windows
    group:
      type: Contact
      function: OR
      parameters:
        - OPEN
        - CLOSED

  gInternalDoors:
    type: Group
    label: Internal Doors
    group:
      type: Contact
      function: OR
      parameters:
        - OPEN
        - CLOSED

  gSecuritySensors:
    type: Group
    label: Security Sensors
    group:
      type: Contact
      function: OR
      parameters:
        - OPEN
        - CLOSED

  gLeftOpenAlert:
    type: Group
    label: Left Open Alert
    group:
      type: Contact
      function: OR
      parameters:
        - OPEN
        - CLOSED

  gSecurityLights:
    type: Group
    label: Security Lights
    group:
      type: Switch
      function: OR
      parameters:
        - ON
        - OFF

  gLights:
    type: Group
    label: Lights
    group:
      type: Switch
      function: OR
      parameters:
        - ON
        - OFF

  gInsideLights:
    type: Group
    label: Inside Lights
    group:
      type: Switch
      function: OR
      parameters:
        - ON
        - OFF

  gOutsideLights:
    type: Group
    label: Outside Lights
    group:
      type: Switch
      function: OR
      parameters:
        - ON
        - OFF

  gLivingRoomLights:
    type: Group
    label: Living Room Lights
    group:
      type: Switch
      function: OR
      parameters:
        - ON
        - OFF

  gLoungeRoomLights:
    type: Group
    label: Lounge Room Lights
    group:
      type: Switch
      function: OR
      parameters:
        - ON
        - OFF

  gMasterBedRoomLights:
    type: Group
    label: Master Bed Room Lights
    group:
      type: Switch
      function: OR
      parameters:
        - ON
        - OFF

  gBackYardLights:
    type: Group
    label: Back Yard Lights
    group:
      type: Switch
      function: OR
      parameters:
        - ON
        - OFF
    groups:
      - gOutsideLights
    metadata:
      synonyms: Back Porch Lights
      alexa: Light
      ga: Light

  gBackPorch_Switch2:
    type: Group
    label: Back Porch Switch 2
    group:
      type: Switch
      function: OR
      parameters:
        - ON
        - OFF

  gMasterBedRoom_CeilingFan:
    type: Group
    label: Master Bed Room Ceiling Fan
    group:
      type: Switch
      function: OR
      parameters:
        - ON
        - OFF

  gIndoorHumidity:
    type: Group
    label: Indoor Humidity
    group:
      type: Number
      function: MAX

  gExhaustFans:
    type: Group
    label: Exhaust Fans

  gExhaustFanButtons:
    type: Group
    label: Exhaust Fan Buttons

  gLightButtons:
    type: Group
    label: Light Buttons

  gTemperature:
    type: Group
    label: Temperature

  gHumidity:
    type: Group
    label: Humidity

  gDimmers:
    type: Group
    label: Dimmers

  gBrightness:
    type: Group
    label: Brightness

  gIPAddress:
    type: Group
    label: IP Address

  gPresenceSimulators:
    type: Group
    label: Presence Simulators

  gGroupedSwitches:
    type: Group
    label: Grouped Switches

  gDoorBell:
    type: Group
    label: Door Bell

Two more new features coming up to 5.3 to simplify package writing:

Before this, I’d have to write custom expressions to “combine” attributes from the package and the package consumer overrides, key-by-key manually. It will now be handled more gracefully with deep merge + !default tag.

Copied from the doc:

Package Composition Example

Package File (light_item.inc.yaml):

variables:
  thingid: ${package_id | lower | replace("_", "-")}
items:
  ${package_id}:
    type: Switch
    # Allow consumer to override label and icon
    label: !default ${package_id | label}
    icon: !default light
    # Automatically append consumer-defined tags and groups
    tags: [Control, Light]
    groups: [MainEquipment]
    autoupdate: false # Without !default, package default wins (consumer override ignored)
    channel: mqtt:topic:${thingid}:power
    !deep <<: ${ARGS} # Deep merge customizations passed from consumer
    metadata:
      ga: Light

Main File (lights.yaml):

packages:
  Kitchen_Light: !include
    file: light_item.inc.yaml
    vars:
      # Customize the package
      label: Main Kitchen Light
      icon: kitchen
      tags: [MainLight]
      groups: [Kitchen]
      autoupdate: true
      metadata:
        alexa: Light
Output
items:
  Kitchen_Light:
    type: Switch
    label: Main Kitchen Light
    icon: kitchen
    tags:
      - Control
      - Light
      - MainLight
    groups:
      - MainEquipment
      - Kitchen
    autoupdate: false
    channel: mqtt:topic:kitchen-light:power
    metadata:
      ga: Light
      alexa: Light