The Automation APIs may be accessed through the Home APIs for Android,
but since their entry point is through a structure, permission must first be
granted on the structure before they can be used.

Once permissions are granted for a structure, import these packages into your
app:  


    import com.google.home.Home
    import com.google.home.HomeDevice
    import com.google.home.Id
    import com.google.home.Structure

A structure contains a
[`HasAutomations`](https://developers.home.google.com/reference/kotlin/com/google/home/automation/HasAutomations)
interface with the following automation-specific methods:

| API | Description |
|---|---|
| `automations()` | List all automations that belong to the structure. Only automations you have created through the Home APIs are returned. |
| `createAutomation(automation)` | Create an automation instance for a structure. |
| `deleteAutomation(automationId)` | Delete an automation instance by its ID. |

<br />

| **Caution:** Code examples shown here may be implemented differently than those of any sample apps, in order to illustrate basic API calls.

<br />

## Create an automation

After creating an instance of Home and receiving permissions from the user, get
the structure and device(s):  

    val structure = homeManager.structures().list().single()
    val device = homeManager.devices().get(Id("myDevice"))!!

Then define the logic of your automation using Automation DSL. In the Home APIs,
an automation is represented by the `Automation` interface. This interface
contains a set of properties:

- Metadata, such as name and description.
- Flags that indicate, for example, whether or not the automation can be executed.
- A list of nodes that contain the logic of the automation, called the automation graph, represented by the `automationGraph` property.

`automationGraph`, by default, is of the type `SequentialFlow`, which is a class
that contains a list of nodes that execute in sequential order. Each node
represents an element of the automation, such as a starter, condition, or
action.

Assign the automation a `name` and `description`.

Creation of an automation defaults the `isActive` flag to `true`, therefore it's
not necessary to explicitly set this flag unless you initially want the
automation to be disabled. In that scenario, set the flag to `false` during
creation.

The `DraftAutomation` interface is used for building and creating automations,
and the `Automation` interface is used for retrieval. For example, here's the
Automation DSL for an automation that turns a device on when another device is
turned on:  

    import com.google.home.automation.Action
    import com.google.home.automation.Automation
    import com.google.home.automation.Condition
    import com.google.home.automation.DraftAutomation
    import com.google.home.automation.Equals
    import com.google.home.automation.Node
    import com.google.home.automation.SequentialFlow
    import com.google.home.automation.Starter
    import com.google.home.Home
    import com.google.home.HomeDevice
    import com.google.home.HomeManager
    import com.google.home.Id
    import com.google.home.matter.standard.OnOff
    import com.google.home.Structure

    ...

    val automation: DraftAutomation = automation {
      name = "MyFirstAutomation"
      description = "Turn on a device when another device is turned on."
      sequential {
        val starterNode = starter<_>(device1, OnOffLightDevice, trait=OnOff)
        condition() { expression = stateReaderNode.onOff equals true }
        action(device2, OnOffLightDevice) { command(OnOff.on()) }
      }
    }

Once the automation DSL is defined, pass it to the
[`createAutomation()`](https://developers.home.google.com/reference/kotlin/com/google/home/automation/HasAutomations#createAutomation(com.google.home.automation.Automation))
method to create the `DraftAutomation` instance:  

    val createdAutomation = structure.createAutomation(automation)

From here, you can use all the other automation methods on the automation, such
as `execute()`, `stop()`, and `update()`.

### Validation errors

If automation creation does not pass validation, a warning or error message
provides information about the issue. For more information, refer to the
[`ValidationIssueType` reference](https://developers.home.google.com/reference/kotlin/com/google/home/automation/ValidationIssueType).

## Code examples

Here we present some example code that could be used to implement parts of the
hypothetical automations described on the
[Design an automation on Android](https://developers.home.google.com/apis/android/automation/design)
page.

### Simple automation

[An automation that raises the blinds at 8:00am](https://developers.home.google.com/apis/android/automation/design#simple_automation)
might be implemented like this:  

    // get all the automation node candidates in the structure
    val allCandidates = structure.allCandidates().first()
    // determine whether a scheduled automation can be constructed
    val isSchedulingSupported =
      allCandidates.any {
        it is EventCandidate &&
          it.eventFactory == Time.ScheduledTimeEvent &&
          it.unsupportedReasons.isEmpty()
      }
    // get the blinds present in the structure
    val blinds =
      allCandidates
        .filter {
          it is CommandCandidate &&
            it.commandDescriptor == WindowCoveringTrait.UpOrOpenCommand &&
            it.unsupportedReasons.isEmpty()
        }
        .map { it.entity }
        .filterIsInstance<HomeDevice>()
        .filter { it.has(WindowCoveringDevice) }
     if (isSchedulingSupported && blinds.isNotEmpty()) {
      // Proceed to create automation
      val automation: DraftAutomation = automation {
        name = "Day time open blinds"
        description = "Open all blinds at 8AM everyday"
        isActive = true
        sequential {
          // At 8:00am local time....
          val unused =
            starter(structure, Time.ScheduledTimeEvent) {
              parameter(Time.ScheduledTimeEvent.clockTime(LocalTime.of(8, 0, 0, 0)))
            }
            // ...open all the blinds
           parallel {
            for (blind in blinds) {
              action(blind, WindowCoveringDevice) { command(WindowCovering.upOrOpen()) }
            }
          }
        }
      }
       val createdAutomation = structure.createAutomation(automation)
    } else if (!isSchedulingSupported) {
      // Cannot create automation.
      // Set up your address on the structure, then try again.
    } else {
      // You don't have any WindowCoveringDevices.
      // Try again after adding some blinds to your structure.
    }

### Complex automation

[An automation that triggers blinking lights when motion is
detected](https://developers.home.google.com/apis/android/automation/design#complex_automation) might be
implemented like this:  

    import com.google.home.Home
    import com.google.home.HomeClient
    import com.google.home.HomeDevice
    import com.google.home.HomeManager
    import com.google.home.Id
    import com.google.home.Structure
    import com.google.home.automation.action
    import com.google.home.automation.automation
    import com.google.home.automation.equals
    import com.google.home.automation.parallel
    import com.google.home.automation.starter
    import com.google.home.google.AssistantBroadcast
    import com.google.home.matter.standard.OnOff
    import com.google.home.matter.standard.OnOff.Companion.toggle
    import com.google.home.matter.standard.OnOffLightDevice
    import java.time.Duration

    // get all the automation node candidates in the structure
    val allCandidates = structure.allCandidates().first()

    // get the lights present in the structure
    val availableLights = allCandidates.filter {
       it is CommandCandidate &&
       it.commandDescriptor == OnOffTrait.OnCommand
    }.map { it.entity }
    .filterIsInstance<HomeDevice>()
    .filter {it.has(OnOffLightDevice) ||
             it.has(ColorTemperatureLightDevice) ||
             it.has(DimmableLightDevice) ||
             it.has(ExtendedColorLightDevice)}

    val selectedLights = ... // user selects one or more lights from availableLights

    automation {
    isActive = true

    sequential {
       // If the presence state changes...
       val starterNode = starter<_>(structure, AreaPresenceState)
       // ...and if the area is occupied...
       condition() {
          expression = starterNode.presenceState equals PresenceState.PresenceStateOccupied
       }
       // "blink" the light(s)
       parallel {
                for(light in selectedLights) {
                action(light, OnOffLightDevice) { command(OnOff.toggle()) }
                delayFor(Duration.ofSeconds(1))
                action(light, OnOffLightDevice) { command(OnOff.toggle()) }
                delayFor(Duration.ofSeconds(1))
                action(light, OnOffLightDevice) { command(OnOff.toggle()) }
                delayFor(Duration.ofSeconds(1))
                action(light, OnOffLightDevice) { command(OnOff.toggle())}
             }
          }
       }
    }

## Dynamically select devices with entity filters

When writing an automation, you aren't limited to specifying specific devices.
A feature called *entity filters* allows your automation to select devices at
runtime based on various criteria.

For example, using entity filters, your automation could target:

- all devices of a particular device type
- all devices in a particular room
- all devices of a particular device type in a particular room
- all devices that are turned on
- all devices that are turned on in a particular room

To use entity filters:

1. On either [`Structure`](https://developers.home.google.com/reference/kotlin/com/google/home/Structure) or [`Room`](https://developers.home.google.com/reference/kotlin/com/google/home/Room), call `atExecutionTime()`. This returns a `TypedExpression<TypedEntity<StructureType>>`.
2. On this object, call [`getDevicesOfType()`](https://developers.home.google.com/reference/kotlin/com/google/home/automation/GetDevicesOfType), passing it a [`DeviceType`](https://developers.home.google.com/reference/kotlin/com/google/home/DeviceType).

Entity filters may be used in starters, state readers, and actions.

For example, to have any On/Off Light trigger an automation from a starter:  

```kotlin
// If any light is turned on or off
val starter =
  starter(
    entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice),
    trait = OnOff,
  )
```

To capture the `OnOff` state of all the lights in a structure (specifically,
On/Off lights) in a state reader:  

```kotlin
// Build a Map<Entity, OnOff>
val onOffStateOfAllLights =
  stateReader(
    entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice),
    trait = OnOff,
  )
```

To get the lights in a particular room and use them in a condition:  

```kotlin
val livingRoomLights =
  stateReader(
    entityExpression = livingRoom.atExecutionTime().getDevicesOfType(OnOffLightDevice),
    trait = OnOff,
  )
// Are any of the lights in the living room on?
condition { expression = livingRoomLights.values.any { it.onOff equals true } }
```

At runtime:

| Scenario | Result |
|---|---|
| No devices meet the criteria in a starter. | The automation doesn't trigger. |
| No devices meet the criteria in a state reader. | The automation starts but will continue based on the condition node. |
| No devices meet the criteria in an action. | The automation starts but the action doesn't do anything. |

The following example is an automation that turns off all lights except the hall
light whenever an individual light is turned off:  

```kotlin
val unused = automation {
  sequential {
    // If any light is turned on or off
    val starter =
      starter(
        entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice),
        trait = OnOff,
      )
    condition {
      // Check to see if the triggering light was turned off
      expression = starter.onOff equals false
    }
    // Turn off all lights except the hall light
    action(
      entityExpression =
        structure.atExecutionTime().getDevicesOfType(OnOffLightDevice).filter {
          it notEquals entity(hallwayLight, OnOffLightDevice)
        }
    ) {
      command(OnOff.on())
    }
  }
}
```

## Execute an automation

Run a created automation using the
[`execute()`](https://developers.home.google.com/reference/kotlin/com/google/home/automation/Automation#execute())
method:  

    createdAutomation.execute()

If the automation has a
[manual starter](https://developers.home.google.com/apis/android/automation/dsl/basic#manual_starter), `execute()`
starts the automation from that point, ignoring all nodes that precede the
manual starter. If the automation doesn't have a manual starter, execution
starts from the node following the first starter node.

If the `execute()` operation fails, a `HomeException` may be thrown. See [Error
handling](https://developers.home.google.com/apis/android/error-handling).

## Stop an automation

Stop a running automation using the `stop()` method:  


    createdAutomation.stop()

If the `stop()` operation fails, a `HomeException` may be thrown. See [Error
handling](https://developers.home.google.com/apis/android/error-handling).

## Get a list of automations for a structure

Automations are defined at the structure level. Collect on the structure's
[`automations()`](https://developers.home.google.com/reference/kotlin/com/google/home/automation/HasAutomations#automations())
to access a `Flow` of automations:  


    import com.google.home.automation.Automation
    import com.google.home.Home
    import com.google.home.HomeDevice
    import com.google.home.HomeManager
    import com.google.home.Id
    import com.google.home.Structure

    ...

    val structure = homeManager.structures().list().single()
    structure.automations().collect {
      println("Available automations:")
      for (automation in it) {
        println(String.format("%S %S", "$automation.id", "$automation.name"))
      }
    }

Alternatively, assign it to a local `Collection`:  

    import com.google.home.automation.Automation
    import com.google.home.Home
    import com.google.home.HomeDevice
    import com.google.home.HomeManager
    import com.google.home.Id
    import com.google.home.Structure

    ...

    var myAutomations: Collection<Automation> = emptyList()
    myAutomations = structure.automations()

## Get an automation by ID

To get an automation by automation ID, call the
[`automations()`](https://developers.home.google.com/reference/kotlin/com/google/home/automation/HasAutomations#automations())
method on the structure, and match on ID:  

    import com.google.home.automation.Automation
    import com.google.home.Home
    import com.google.home.HomeDevice
    import com.google.home.HomeManager
    import com.google.home.Id
    import com.google.home.Structure

    ...

    val structure = homeManager.structures().list().single()
    val automation: DraftAutomation = structure.automations().mapNotNull {
      it.firstOrNull
        { automation -> automation.id == Id("automation-id") }
      }.firstOrNull()

Response:  

    // Here's how the automation looks like in the get response.
    // Here, it's represented as if calling a println(automation.toString())

    Automation(
      name = "automation-name",
      description = "automation-description",
      isActive = true,
      id = Id("automation@automation-id"),
      automationGraph = SequentialFlow(
        nodes = [
          Starter(
            entity="device@test-device",
            type="home.matter.0000.types.0101",
            trait="OnOff@6789..."),
          Action(
            entity="device@test-device",
            type="home.matter.0000.types.0101",
            trait="OnOff@8765...",
            command="on")
        ]))

## Get an automation by name

The
[`filter()`](https://kotlinlang.org/docs/collection-filtering.html#partition)
method in Kotlin can be used to further refine API calls. To get an automation
by name, get the structure's automations and filter on the automation name:  

    import com.google.home.automation.Automation
    import com.google.home.Home
    import com.google.home.HomeDevice
    import com.google.home.HomeManager
    import com.google.home.Id
    import com.google.home.Structure

    ...

    val structure = homeManager.structures().list().single()
    val automation: DraftAutomation = structure.automations().filter {
      it.name.equals("Sunset Blinds") }

## Get all automations for a device

To get all the automations that reference a given device, use nested filtering
to scan each automation's `automationGraph`:  

    import android.util.Log
    import com.google.home.Home
    import com.google.home.HomeDevice
    import com.google.home.HomeManager
    import com.google.home.Id
    import com.google.home.Structure
    import com.google.home.automation.Action
    import com.google.home.automation.Automation
    import com.google.home.automation.Automation.automationGraph
    import com.google.home.automation.Node
    import com.google.home.automation.ParallelFlow
    import com.google.home.automation.SelectFlow
    import com.google.home.automation.SequentialFlow
    import com.google.home.automation.Starter
    import com.google.home.automation.StateReader

    ...

    fun collectDescendants(node: Node): List<Node> {
      val d: MutableList<Node> = mutableListOf(node)

      val children: List<Node> =
        when (node) {
          is SequentialFlow -> node.nodes
          is ParallelFlow -> node.nodes
          is SelectFlow -> node.nodes
          else -> emptyList()
        }
      for (c in children) {
        d += collectDescendants(c)
      }
      return d
    }

    val myDeviceId = "device@452f78ce8-0143-84a-7e32-1d99ab54c83a"
    val structure = homeManager.structures().list().single()
    val automations =
      structure.automations().first().filter {
        automation: Automation ->
        collectDescendants(automation.automationGraph!!).any { node: Node ->
          when (node) {
            is Starter -> node.entity.id.id == myDeviceId
            is StateReader -> node.entity.id.id == myDeviceId
            is Action -> node.entity.id.id == myDeviceId
            else -> false
          }
        }
      }

| **Note:** [`structure.automations()`](https://developers.home.google.com/reference/kotlin/com/google/home/automation/HasAutomations#automations()) only lists automations created by you through the Home APIs. Automations created by other solution providers for the same structure are not returned.

## Update an automation

To update an automation's metadata, call its
[`update()`](https://developers.home.google.com/reference/kotlin/com/google/home/Updatable#update(kotlin.method1,kotlin.method1))
method, passing it a lambda expression that sets the metadata:  

    import com.google.home.automation.Automation
    import com.google.home.Home
    import com.google.home.HomeDevice
    import com.google.home.HomeManager
    import com.google.home.Id
    import com.google.home.Structure

    ...

    val structure = homeManager.structures().list().single()
    val automation: DraftAutomation = structure.automations().mapNotNull {
      it.firstOrNull
        { automation -> automation.id == Id("automation-id") }
      }.firstOrNull()
    automation.update { this.name = "Flashing lights 2" }

The [`update()`](https://developers.home.google.com/reference/kotlin/com/google/home/Updatable#update(kotlin.method1,kotlin.method1))
method supports fully replacing an automation graph, but not per-node editing of
the graph. Per-node editing is error-prone due to node interdependencies. If
you want to change the logic of an automation, generate a new graph and fully
replace the existing one.  

    import com.google.home.automation.Automation
    import com.google.home.Home
    import com.google.home.HomeDevice
    import com.google.home.HomeManager
    import com.google.home.Id
    import com.google.home.Structure

    ...

    val structure = homeManager.structures().list().single()
    val automation: Automation = structure.automations().mapNotNull {
      it.firstOrNull
        { automation -> automation.id == Id("automation-id") }
      }.firstOrNull()
    automation.update {
      this.automationGraph = sequential {
        val laundryWasherCompletionEvent =
          starter<_>(laundryWasher, LaundryWasherDevice, OperationCompletionEvent)
        condition {
          expression =
            laundryWasherCompletionEvent.completionErrorCode equals
              // UByte 0x00u means NoError
              0x00u
        }
        action(speaker, SpeakerDevice) { command(AssistantBroadcast.broadcast("laundry is done")) }
        }
      }
    }

| **Key Point:** Updating an automation does not make incremental changes to the automation. Instead, a new, immutable automation is created, incorporating the change(s), and then saved, overwriting the original automation. Therefore, developers should avoid maintaining references to automation instances, and instead rely on the `structure.automations()` collection whenever they need to reference an automation.

## Delete an automation

To delete an automation, use the structure's
[`deleteAutomation()`](https://developers.home.google.com/reference/kotlin/com/google/home/automation/HasAutomations#deleteAutomation(com.google.home.Id))
method. An automation must be deleted using its ID.  

    import com.google.home.automation.Automation
    import com.google.home.Home
    import com.google.home.HomeDevice
    import com.google.home.HomeManager
    import com.google.home.Id
    import com.google.home.Structure

    ...

    val structure = homeManager.structures().list().single()
    val automation: DraftAutomation = structure.automations().first()
    structure.deleteAutomation(automation.id)

If the deletion fails, a `HomeException` may be thrown. See [Error
handling](https://developers.home.google.com/apis/error-handling).

## Impact of device deletion on automations

If a user deletes a device that is used in an automation, the deleted device
can't trigger any starters, and the automation won't be able to read attributes
from it, or issue commands to it. For example, if a user deletes an
[`OccupancySensorDevice`](https://developers.home.google.com/reference/kotlin/com/google/home/matter/standard/OccupancySensorDevice)
from their home, and an automation has a starter that depends on
the `OccupancySensorDevice`, that starter can no longer activate the automation.