<br />

> [!NOTE]
> **Note:** An application can only be granted permission to one structure at any given time.

<br />

Structure APIs may be accessed through the Home APIs for Android. Import these
packages into your app:

```kotlin
import com.google.home.Home
import com.google.home.Id
import com.google.home.Structure
```

## Error handling

<br />

Any method in the Home APIs can throw a
[`HomeException`](https://developers.home.google.com/reference/kotlin/com/google/home/HomeException.md.txt), so we recommend that you use a `try-catch` block to
catch `HomeException` on all calls.

When handling `HomeException`, check its
[`error.code`](https://developers.home.google.com/reference/kotlin/com/google/home/HomeError.md.txt#code()) and [`error.message`](https://developers.home.google.com/reference/kotlin/com/google/home/HomeError.md.txt#message()) fields to learn what went wrong. There may be sub-error codes
as well, so call the [`getSubErrorCodes()`](https://developers.home.google.com/reference/kotlin/com/google/home/HomeException.md.txt#getSubErrorCodes()) method and check the result.

Any unhandled exceptions will result in your app crashing.

For more information, see
[Error handling](https://developers.home.google.com/apis/android/error-handling.md.txt).

<br />

## Sample calls

<br />

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

<br />

### Get a list of structures

Once initialized, a `structures()` call returns a Flow of structures
accessible to you:

```kotlin
// Get a flow of all structures accessible to the user
val allStructuresFlow: HomeObjectsFlow<Structure> = home.structures()

// Calling list() on a HomeObjectsFlow returns the first Set of elements.
val allStructures: Set<Structure> = allStructuresFlow.list()
```

The `structures()` API is a flow that may not immediately return a valid
list of structures. If your app is reactive and subscribes to that flow to
drive the UI, a valid list of structures should eventually be returned.
There are other situations where an empty structure list could be returned, for
example if the user's phone loses connectivity or if the user
has revoked permissions to your app. You should be sure to handle these cases
in your app.

Alternatively, if imperative programming is strongly required instead of
reactive programming, a terminal flow operator can be used:

```kotlin
val everyStructure = withTimeout(5000) { home.structures().first { it.isNotEmpty() } }
```

This call waits for a valid list of structures to come through the flow and
times out if the list is not received within an app-designated timeout.

> [!IMPORTANT]
> **Important:** The Structure API returns only those structures the developer has been granted access to.

### Get structure properties

With the list of structures in hand, you can access the properties for them:

```kotlin
// Get a flow on a structure. Flow emits new values on structure metadata changes: name.
val structureFlow: Flow<Structure> = home.structures().itemFlow(myStructureId)

// Get a snapshot of the structure.
val structure: Structure = structureFlow.first()

// Get structure properties
println("id ${structure.id}")
println("name ${structure.name}")
```

### Find a structure by name

If you know the name of a structure, you can also access it using the `name`
property:

```kotlin
val myHome = home.structures().list().first { it.name == "My home" }
```

From there, properties, rooms, and devices for each structure are accessible.

### Work with multiple structures

To use more than one structure, get a separate reference to each structure:

```kotlin
var structure1: Structure? = null
var structure2: Structure? = null

try {
  structure1 = home.structures().list().firstOrNull { it.name == "Main House" }
} catch (e: HomeException) {
  // Code for handling the exception
}
try {
  structure2 = home.structures().list().firstOrNull { it.name == "Guest Cottage" }
} catch (e: HomeException) {
  // Code for handling the exception
}
```

### Get a list of rooms

With a structure in hand, you can get a list of rooms and access the properties
for them:

```kotlin
val allRoomsFlow: HomeObjectsFlow<Room> = structure.rooms()
val allRooms: Set<Room> = allRoomsFlow.list()
val room: Room = allRooms.first()

println("id ${room.id}")
println("name ${room.name}")
```

### Create a room

To create a new room:

```kotlin
val testName = "Test Room Name"
val newRoom: Room = structure.createRoom(testName)
```

### Delete a room

Or, alternatively, you can delete a room:

```kotlin
val roomToDelete = structure.rooms().list().filter { it.name == "room_id1" }.firstOrNull()
    structure.deleteRoom(roomToDelete!!)
```

You can also delete a room with just an ID:

```kotlin
val roomToDelete1 = allRooms.filter { it.id == testRoomId }.firstOrNull()
structure.deleteRoom(roomToDelete1!!)
```

If a room with devices is deleted, the devices will still be in the structure
but no longer assigned to a room.

### Move devices to a different room

Once you have a structure, you can move devices to a different room within
that structure:

```kotlin
val room2 = structure.rooms().get(Id("room_id_other_structure"))
    val device1 = structure.devices().get(Id("device_id1"))
    structure.moveDevicesToRoom(room2!!, listOf(device1!!))
```

If you only have device and room IDs, you can also move devices:

```kotlin
structure.moveDevicesToRoom(Id("room_id_other_structure"), listOf(Id("device_id1")))
```

### Change the name of a room

Call the [`setName()`](https://developers.home.google.com/reference/kotlin/com/google/home/Room.md.txt#setName(kotlin.String))
method to change the name of a room:

```kotlin
livingRoom.setName("Living Room")
```

Names will be truncated if over the 60 Unicode code point
(character) limit and no errors will be thrown. Developers are responsible for handling long names
and, for example, can decide if they want to inform users names will be truncated.

### View the device types a user has granted permissions for

In the Google Home ecosystem, for most device types, users can grant permissions
for all devices of that type at once. For sensitive or restricted device types,
such as locks, cameras, or doorbells, users must grant permission to them
individually.

To determine whether a user has granted permission to access a sensitive or
restricted device type, use the Structure-level `consentedDeviceTypes()`
function:

```kotlin
import com.google.home.Structure
import com.google.home.DeviceType
import com.google.home.DeviceTypeFactory
import com.google.home.consentedDeviceTypes // Extension function from the SDK
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch

/**
 * Example of how an app may monitor which device types have been granted access by a user.
 */
fun monitorDeviceConsent(structure: Structure, myScope: CoroutineScope) {
    // Obtain the flow of consented device type factories
    val consentedTypesFlow: Flow<Set<DeviceTypeFactory<out DeviceType>>> =
        structure.consentedDeviceTypes()

    myScope.launch {
        consentedTypesFlow.collect { consentedSet ->
            // Check if the user has consented to share a specific restricted
            // type, such as a Doorbell or Camera.
            val hasCameraAccess = consentedSet.any {
                it.toString() == "matter.google.type.GoogleDoorbellDevice"
            }

            if (hasCameraAccess) {
                // Enable features that require camera access
            } else {
                // Inform the user or disable camera-specific features
            }
        }
    }
}
```

## Device groups

A device group represents a user-defined collection of devices within a single
structure. Group membership is retained in Google services and is visible to
other members of the structure and other apps with access to the structure.

Like physical rooms, device groups belong to a single structure. But unlike
rooms, device groups are logical groupings. A device can belong to multiple
device groups simultaneously. Device groups can contain up to 100
devices.
Device groups are represented by the [`DeviceGroup`](https://developers.home.google.com/reference/kotlin/com/google/home/DeviceGroup.md.txt) entity, which is distinct from [`HomeDevice`](https://developers.home.google.com/reference/kotlin/com/google/home/HomeDevice.md.txt). To query and interact with device groups, include `DeviceGroup` in the `entityTypes` list when initializing your [`FactoryRegistry`](https://developers.home.google.com/reference/kotlin/com/google/home/FactoryRegistry.md.txt), along with the [`Group`](https://developers.home.google.com/reference/kotlin/com/google/home/google/Group.md.txt), [`GroupManagement`](https://developers.home.google.com/reference/kotlin/com/google/home/google/GroupManagement.md.txt), and [`GroupMembership`](https://developers.home.google.com/reference/kotlin/com/google/home/google/GroupMembership.md.txt) traits.

You can interact with the devices in a group either by iterating over its
member devices directly (see
[Interact with group member devices](https://developers.home.google.com/apis/android/structure.md.txt#interact-group-members)) or by targeting
a device type part on the `DeviceGroup` (see
[Control a device group](https://developers.home.google.com/apis/android/device/control.md.txt#control-group)).

> [!NOTE]
> **Note:** Device group APIs require the `@HomeExperimentalApi` annotation on Android.

### Get a list of device groups

To observe all device groups in a structure, call `home.entities(DeviceGroup)`
and filter by the structure's ID:

```kotlin
home.entities(DeviceGroup).collect { allGroups ->
  val structureGroups = allGroups.filter { it.structureId == structure.id }
  for (group in structureGroups) {
    val groupName = group.trait(Group)?.Name
    println("Found group: $groupName with ID: ${group.id.id}")
  }
}
```

### Interact with group member devices

Iterating over a group's member devices with `group.devices()` gives you
fine-grained control over individual members, lets you batch commands in a
single request, and lets you aggregate or write attributes across members.

For example, to send a single batched request with commands for a trait across
all compatible members of a group:

```kotlin
val members = group.devices(enableMultipartDevices = true).first()
val onOffTraits = members.mapNotNull { device ->
  device.partOrNull(OnOffLightDevice).first()?.standardTraits?.onOff
}

val responses = home.sendBatchedCommands {
  onOffTraits.map { onOff ->
    add(onOff.onBatchable())
  }
}

for (response in responses) {
  response.getOrThrow()
}
```

You can also update an attribute across all matching member devices in a group
(for example, setting `onTransitionTime` to `1u` on all `DimmableLightDevice`
members) and await completion within a deadline:

```kotlin
val members = group.devices(enableMultipartDevices = true).first()

withTimeout(5.seconds) {
  members.map { device ->
    async {
      val light = device.partOrNull(DimmableLightDevice).first()
      val levelControl = light?.standardTraits?.levelControl
      if (
        levelControl?.supports(
          LevelControl.Attribute.onTransitionTime
        ) == true
      ) {
        levelControl.update { setOnTransitionTime(1u) }
      }
    }
  }.awaitAll()
}
```

To aggregate an attribute across members of a group, such as finding the
minimum `colorTempPhysicalMaxMireds` across all color temperature lights in the
group:

```kotlin
val members = group.devices(enableMultipartDevices = true).first()
val minMaxMireds: UShort? = members.mapNotNull { device ->
  val light = device.partOrNull(ColorTemperatureLightDevice).first()
  light?.standardTraits?.colorControl?.colorTempPhysicalMaxMireds
}.minOrNull()
```

### Create a device group

Use
[`GroupManagement`](https://developers.home.google.com/reference/kotlin/com/google/home/google/GroupManagement.md.txt)
on the structure to create a device group:

```kotlin
val groupManagement = structure.trait(GroupManagementTrait)
val response = groupManagement?.createUserDefinedGroup(
  name = "Living room lights",
  memberDeviceObjectIds = listOf(light1.id.id, light2.id.id)
)
```

### Manage group membership

To add members to an existing group:

```kotlin
structure.trait(GroupManagementTrait)?.addGroupMembers(
  groupObjectId = group.id.id,
  memberDeviceObjectIds = listOf(light3.id.id)
)
```

To remove members from a group:

```kotlin
structure.trait(GroupManagementTrait)?.removeGroupMembers(
  groupObjectId = group.id.id,
  memberDeviceObjectIds = listOf(light1.id.id)
)
```

### Change the name of a device group

To rename a device group, update the name attribute on the group's `GroupTrait`:

```kotlin
group.trait(GroupTrait)?.update {
  name = "Movie corner"
}
```

### Delete a device group

To delete a device group from a structure:

```kotlin
structure.trait(GroupManagementTrait)?.deleteGroup(
  groupObjectId = group.id.id
)
```

> [!NOTE]
> **Note:** Deleting a device group removes the group container, but does not remove or unpair the member devices from the structure.

## Automations

The entry point to the Automation API is through a structure. To learn more
about Automations in the Home APIs, see
[Automation API on Android overview](https://developers.home.google.com/apis/android/automation.md.txt).