Use the following guide to understand how various Automation DSL nodes may be
used to construct an automation.

All automation DSL is placed within a single `automation` node. The
`automation` node forms the boundary between the outer Kotlin language
context and the embedded DSL context.

## Sequential flow

The sequential flow is the default type of automation flow.

![Sequential DSL example](https://developers.home.google.com/static/apis/images/apis-automation-dsl-sequential.svg)

Here's a very basic Automation DSL template that uses a sequential flow
consisting of a starter, a condition, and an action:

```kotlin

import com.google.home.automation.action
import com.google.home.automation.automation
import com.google.home.automation.condition
import com.google.home.automation.sequential
import com.google.home.automation.starter

...

automation {
  sequential {
    starter<_>(...)
    condition {...}
    action {...}
  }
}
```

This can be refined by adding additional nodes.

## Starter

Starter nodes define the initial circumstances that activate an automation. For
example, a change in state or value. An automation must have at least one
starter, otherwise it will fail validation. In order to add more than one
starter to an automation, you must use a `select` node.

### Starter based on trait attribute

When declaring a starter node that's based on a trait attribute, specify:

- the device
- the device type to which the trait belongs
- the trait

```kotlin
starter<_>(thermostat, TemperatureSensorDevice, TemperatureMeasurement)
```

The device type parameter is required because it lets you specify which device
type within a device the automation addresses. For example, a device might be
composed of a
[`FanDevice`](https://developers.home.google.com/reference/kotlin/com/google/home/matter/standard/FanDevice.md.txt)
and a
[`HeatingCoolingUnitDevice`](https://developers.home.google.com/reference/kotlin/com/google/home/matter/standard/HeatingCoolingUnitDevice.md.txt),
both of which contain the
[`OnOff`](https://developers.home.google.com/reference/kotlin/com/google/home/matter/standard/OnOff.md.txt)
trait. By specifying the device type, there is no ambiguity about which part of
the device triggers the automation.

### Starter based on event

When declaring a starter node that's based on an event, specify:

- the device
- the device type to which the trait belongs
- the event

```kotlin
starter<_>(doorBell, GoogleDoorbellDevice, DoorbellPressed)
```

### Starter based on a structure and event, with parameters

Some events can have parameters, so these parameters also need to be included
in the starter.

For example, this starter uses the
`Time` trait's [`ScheduledTimeEvent`](https://developers.home.google.com/reference/kotlin/com/google/home/google/TimeTrait.ScheduledTimeEvent.md.txt) to activate the automation at 7:00am:

```kotlin
val earlyMorning = starter<_>(structure, Time.ScheduledTimeEvent) {
  parameter(Time.ScheduledTimeEvent.clockTime(
    LocalTime.of(7, 0, 0, 0)))
}
```

### Starter based on weather

You can specify current or forecasted weather conditions in a starter, using
the Weather trait:

```kotlin
val weatherState = starter<_>(structure, trait = Weather)
```

See [Close the blinds if it is likely to rain](https://developers.home.google.com/apis/android/automation/examples.md.txt#close-blinds-if-rain)
on the [Example automations](https://developers.home.google.com/apis/android/automation/example) page.

### Manual starter

A manual starter is a special type of starter that lets the user manually
run the automation.

When declaring a manual starter:

- Don't specify a trait or device type.
- Provide a UI element that calls [`Automation.execute()`](https://developers.home.google.com/apis/android/automation.md.txt#execute_an_automation).

When placing a manual starter in a `select` flow along with another starter, the
manual starter overrides the other starter:

```kotlin
select {
  manualStarter()
  starter<_>(thermostat, TemperatureSensorDevice, TemperatureMeasurement)
}
```

Note that any `condition` nodes following a manual starter will be evaluated,
and could block execution of the automation, depending on the `condition`
expression.

![Separating a manual starter from a conditional](https://developers.home.google.com/static/apis/images/apis-automation-dsl-select-manual-starter-vertical.svg)

One way to structure your automation so that `condition` nodes don't block an
automation that was activated with a manual starter is to put the other starter
in a separate sequential flow along with its `condition`:

```kotlin
automation_graph {
  sequential {
    select {
      sequential {
        starter<_>(...)
        condition {...}
      }
      sequential {
        manualStarter()
      }
    }
    action {...}
  }
}
```

## Reference the value of an attribute

To use the value of an attribute in an expression, use the following syntax.

With a `stateReader`:

```kotlin
val time = stateReader<_>(structure, Structure, Time)
val currTime = time.currentTime
```

With a `starter`:

```kotlin
val starterNode = starter<_>(device1, LaundryWasherDevice, OnOff)
condition() {
  expression = starterNode.onOff equals true
}
```

## Condition nodes and expressions

A condition node represents a decision point that determines whether the
automation continues or not. An automation can have multiple `condition` nodes.
If any `condition` node's expression evaluates to `false`, execution of the
entire automation ends.

Within a `condition` node, you can combine multiple condition criteria using
various [operators](https://developers.home.google.com/apis/android/automation/dsl/operators.md.txt), as long as the expression
evaluates to a single
boolean value. If the resulting value is `true`, the condition is met and the
automation continues execution of the next node. If it's `false`, the automation
stops executing at that point.

Expressions are formed similarly to expressions in Kotlin, and may contain
primitive values such as numbers, characters, strings, and booleans, as well as
Enum values. Grouping sub expressions with parentheses lets you control the
order in which they're evaluated.

Here's an example of a `condition` which combines multiple subexpressions into a
single expression:

```kotlin
condition() {
  val expr1 = starterNode.lockState equals DlLockState.Unlocked
  val expr2 = stateReaderNode.lockState equals true

  val expr3 = occupancySensingDevice.occupied notEquals 0
  val expr4 = timeStateReaderNode
    .currentTime
    .between(
      timeStateReaderNode.sunsetTime,
      timeStateReaderNode.sunriseTime)
  expression = (expr1 and expr2) or (expr3 and expr4)
}
```

You may reference the value of a trait accessed through a starter:

```kotlin
val starterNode = starter<_>(device, OnOff)
condition() { expression = starterNode.onOff equals true }
```

### stateReader

The other way to reference trait attribute values in a `condition` node is with
a `stateReader` node.

To do this, first capture the trait attribute value in a `stateReader` node. A
`stateReader` takes the `structure` and the trait as arguments:

```kotlin
import com.google.home.automation.stateReader
...
val filterMonitoringState = stateReader<_>(structure, ActivatedCarbonFilterMonitoring)
```

Then reference the `stateReader` in the `condition` node:

```kotlin
condition() {
  expression =
    filterMonitoringState.changeIndication
      .equals(ChangeIndicationEnum.Warning)
}
```

Using [comparison](https://developers.home.google.com/apis/android/automation/dsl/operators.md.txt#comparison_operators) and
[logical operators](https://developers.home.google.com/apis/android/automation/dsl/operators.md.txt#logical_operators), multiple
`stateReaders` may be used in a `condition` node:

```kotlin
val armState = stateReader<_>(doorLock, DoorLockDevice, ArmDisarm )
val doorLockState = stateReader<_>(doorLock, DoorLockDevice, DoorLock)
condition() {
  expression =
    (armState.armState equals true)
    and
    (doorLockState.lockState equals true)
}
```

### Condition duration

In addition to a boolean expression in a condition, you can specify a timeframe
during which the expression must be true in order to run the automation. For
example, you can define a condition that fires only if a light has been on for
ten minutes.

```kotlin
  condition {
    expression(lightStateReader.onOff == true)
    forDuration(Duration.ofMinutes(10))
  }
```

The duration can range from 5 seconds to 24 hours.

## Action nodes

The action node is where the work of the automation takes place.
In this example, the action invokes the
[`AssistantBroadcast`](https://developers.home.google.com/reference/kotlin/com/google/home/google/AssistantBroadcast.md.txt)
trait's
[`broadcast()`](https://developers.home.google.com/reference/kotlin/com/google/home/google/AssistantBroadcast.md.txt#broadcast(kotlin.String)) command:

```kotlin
action(device, SpeakerDevice) {
  command(AssistantBroadcast.broadcast("Intruder detected!"))
}
```

## Import statements

When developing automations, it isn't always obvious how to import various
elements of the Home APIs into your code.

Trait attributes are imported from the trait's `Companion` object:

```kotlin
import com.google.home.matter.standard.OnOff.Companion.onOff
```

Data structures that are defined by a trait are imported from the trait class
whose name ends in "-Trait":

```kotlin
import com.google.home.matter.standard.MediaPlaybackTrait.PlaybackStateEnum
```

Trait commands are imported from the trait's `Companion` object:

```kotlin
import com.google.home.matter.standard.Thermostat.Companion.setTemperatureSetpointHold
```