DSL concepts

This is an overview of the fundamental concepts of the Automation DSL.

Automation components

An automation consists of the following basic components, typically evaluated in this order:

  1. Starter — Defines the initial conditions that activate the automation, such as a change to a trait. An automation must have a starter.
  2. Condition — Any additional constraints to evaluate after an automation has been activated. The expression in a Condition must evaluate to true in order for the actions of an automation to proceed.
  3. Action — Commands or state updates that are performed when all conditions have been met.

For example, perhaps you have an automation that dims the lights in a room when the TV in that room is turned on between sunset and sunrise. In this example:

  1. Starter — The TV was turned on, which is a change in state on a TV trait.
  2. Condition— The current time for the home the TV is in is evaluated.
  3. Action — The lights in the same room as the TV are dimmed.

The automation would be activated when the TV in the room is turned on, but the automation only executes if the condition of "time is between sunset and sunrise" is met.

In addition to the basic structure, automations in the Home APIs also contain metadata, such as name and description, which can be used to identify them for developers and users.

Nodes

In the Home APIs, the logical structure of an automation consists of nodes. Nodes are abstract, reusable units that represent entity behaviors or execution flows. Each node can have input variables, as well as output variables that may be consumed by other nodes.

Table: Types of automation nodes
Node Node Type Kotlin implementation Description
Starter Behavioral StarterNodeDsl Starts an automation when a trait's state (any attribute) changes.
StateReader Behavioral StateReaderNodeDsl Reads a trait attribute and lets you to capture its value for use in condition nodes.
Action Behavioral ActionNodeDsl Invokes trait command(s).
Sequential Execution flow SequentialFlow Executes nested action nodes in sequence. This is the default execution behavior.
Parallel Execution flow ParallelFlow Executes nested action nodes in parallel.
Condition Execution flow ConditionNodeDsl Conditionally change execution flow based on evaluations of logical expressions. Conditions may be associated with a starter (starter specific conditions) or be global (apply to all starters).
Select Execution flow SelectFlow Allows more than one starter to activate an automation.
Expression Value Expression May be the value of a trait's attribute, a constant, or a literal value, and must evaluate to a list, number, boolean, or string.

Behavioral nodes

Nodes such as starters and actions are behavioral nodes. Starters activate an automation based on device attribute changes. Actions issue device commands or update attributes.

Behavioral nodes are typically tied to device traits and output trait state for use as input in other nodes.

Execution flow nodes

Some nodes represent execution flows, such as sequential and parallel. Each of these nodes contain the behavioral nodes that define the automation.

For example, a sequential flow may contain nodes that execute in sequential order. Typically, these would be starter, condition, and action.

Sequential execution flows
Figure 1: Sequential automation flow

A parallel flow may have multiple action nodes executing at the same time, such as turning on multiple lights at the same time. Nodes following a parallel flow won't execute until all branches of the parallel flow finish.

Parallel execution flows
Figure 2: Parallel automation flow

Another type of execution flow is a condition flow, which can change the execution flow based on the evaluation of an expression.

For example, perhaps you have an automation that performs an action based on whether it is nighttime. A condition node checks the time of day, then follows the appropriate execution path based on that evaluation.

Condition flow
Figure 3: Condition flow

A select flow is useful when you want to have more than one starter that can activate your automation. When you enclose two or more starters in a select flow, any one of the starters can activate the automation.

For example, you could write an automation that lowers the blinds at sunset, if the temperature rises above a certain threshold, or if the brightness exceeds a threshold. Three separate starters handle each of these scenarios, and all three would be wrapped in a select flow.

Select flow
Figure 4: Select flow

Nested flows

In complex automations, execution flow nodes can also be nested. For example, you may have a sequential flow that executes a parallel flow.

Nested execution flows
Figure 5: Nested execution flows

DSL nodes can be nested and combined in various ways to meet your specific needs, according to constraints outlined in the following table. The Builder column links to the Kotlin typesafe builder documentation, which details what is allowed for use in each type of node.

Table: How nodes may be combined
Node Builder May contain the following node type and data Must be within one of the following node types
Starter AutomationBuilder Expression Select, Sequential
ManualStarter AutomationBuilder Select, Sequential
StateReader AutomationBuilder Expression (typically consisting of a trait attribute value) Action, Condition
Action ActionBuilder Command, Entity, Expression Parallel, Select, Sequential
Sequential SequentialFlowBuilder Parallel, Select, Sequential
Parallel ParallelFlowBuilder Action Sequential
Condition ConditionBuilder Expression Parallel, Sequential
Select AutomationBuilder Condition, Sequential, Starter, ManualStarter Sequential, and must be the first node in the flow

Automation DSL

In the Home APIs, automations are defined using the Automation DSL (Domain-Specific Language). The Automation DSL is implemented as a Kotlin DSL (domain-specific language), using Kotlin type-safe builders and is specifically designed for defining automation templates.

When an automation is compiled, Kotlin type-safe builders generate Kotlin data classes that are then serialized to protocol buffer JSON, which is used to make calls to Google's Automation Services.

The Automation DSL simplifies and streamlines the process of building automations. It natively uses the same data model of Matter standard traits and smart home traits featured in the Device API.

The Automation DSL also defines the logic of an automation in terms of abstract device types, as opposed to specific device instances located in a user's home. It allows the developer to provide input parameters that may be used at runtime to specify actual device instances, as well as other important parameter values.

The DSL syntax is similar to Kotlin's, and is equally type-safe, but an automation written in the Automation DSL is simpler and more concise than the same automation written in pure Kotlin.

Example

The following is an example automation that turns on a device, written using the Automation DSL:

val automation = automation {
  name = "MyFirstAutomation"
  description = "If light1 is on, turn on light2."
  isActive = true
  sequential {
    val onOffTrait = starter<_>(device1, OnOffLightDevice, OnOff)
    condition() { expression = onOffTrait.onOff equals true }
    action(device2, OnOffLightDevice) { command(OnOff.on()) }
  }
}

This automation is very basic: when device1, a light, turns on (the onOff attribute changes to true), then sends the on() command to turn device2 on.

The automation uses a sequential node, which indicates that its nodes will run in sequential order.

Within the sequential node are behavioral nodes such as starter, condition, and action. The output of the starter node is assigned to a variable for use in the condition node.