Build a mobile app using the Home APIs on Android

1. Before you begin

The Google Home APIs provide a set of libraries for Android developers to tap into the Google Home ecosystem. With these new APIs, developers can build apps that seamlessly commission and control smart home devices.

Google provides an Android Sample App for developers who want to access a working example using the Google Home APIs. This codelab is based on a branch of the Sample App that walks you through how to use the Permissions, Commissioning, Device, and Structure APIs.

Prerequisites

What you'll learn

  • How to build an Android app using the Google Home APIs with best practices.
  • How to use the Device and Structure APIs to represent and control a smart home.
  • How to use Commissioning APIs to add devices to the Google Home ecosystem.

Optional: Set Up Your Home

Prior to using the Google Home APIs, you'll need to set up a home on your Google Account using the Google Home app, and add a few devices. This section discusses how to do this using the Google Home Playground, which provides virtual smart home devices.

Open home-playground.withgoogle.com in your web browser, sign in with your Google Account and see if the following emulated devices appear:

  • outlet1: On/Off plug
  • light2: Dimmable light
  • light3: On/Off light
  • ac3: Air conditioner
  • blinds4: Window Covering
  • washer5: Smart washer

914d23a42b72df8f.png

Open the Google Home app on your mobile device, tap the Add button, and select Works with Google Home. Search for "playground" in the list, then select the "Google Home Playground" project, and tap Continue.

e9ec257b0d9b1ed2.png29fd7416e274d216.pngd974573af0611fd8.png

The Google Home Playground will show you an account authorization page. Tap Authorize or Sign In with Google. You'll see all the devices you configured from the web app in the mobile app.

13108a3a15440151.png8791a6d33748f7c8.png

Select all the devices and complete the setup process. Returning to the Home page, you'll see all the available devices.

2b021202e6fd1750.png

The supported devices in the list are now available to use with the Google Home APIs.

2. Set up your project

The following diagram illustrates the architecture of a Home APIs app:

Architecture of the Home APIs for an Android app

  • App Code: The core code that developers work on to build the app's user interface and the logic for interacting with the Home APIs SDK.
  • Home APIs SDK: The Home APIs SDK provided by Google works with the Home APIs Service in GMSCore to control smart home devices. Developers build apps that work with the Home APIs by bundling them with the Home APIs SDK.
  • GMSCore on Android: GMSCore, also known as Google Play services, is a Google platform that provides core system services, enabling key functionality on all certified Android devices. The home module of Google Play services contains the services that interact with the Home APIs.

Set up the Home SDK

Follow the steps outlined in Set up the SDK to get the latest SDK.

Get the Sample App

The source code for the Sample App is available on GitHub. This codelab uses the examples from the codelab-branch-1 branch of the Sample App.

Navigate to where you want to save the project and clone the codelab-branch-1 branch:

$ git clone -b codelab-branch-1 https://github.com/google-home/google-home-api-sample-app-android.git

Build the Sample App

Perform steps 1-5 in Build the app.

32f2b3c0cd80fcf1.png

When the app is running successfully on your phone, you'll see the Sample App main page. But you won't be able to sign in until you set up OAuth authentication and implement the missing pieces using the Permission API.

3. Set up Authentication

The Home APIs use OAuth 2.0 to grant access to devices in the structure. OAuth allows a user to grant permission to an app or service without having to expose their login credentials.

Follow the instructions in Set up OAuth consent to configure the consent screen. Be sure to create at least one test account.

Then follow the instructions in Set up OAuth credentials to create the credentials for the app.

4. Initialization and Handling Permissions

In this section, you'll learn how to initialize the SDK and handle user permissions by completing the missing pieces using Permissions API.

Define Supported Types and Traits

When developing an app, you need to explicitly note which device types and traits the app will support. In the Sample App, we do this by defining static lists in the companion object in HomeApp.kt, which then can be referenced throughout the app as needed:

companion object {

  // List of supported device types by this app:
  val supportedTypes: List<DeviceTypeFactory<out DeviceType>> = listOf(
    OnOffLightDevice,
    DimmableLightDevice,

  // ...
  )
  // List of supported device traits by this app:
  val supportedTraits: List<TraitFactory<out Trait>> = listOf(
  OnOff,
  LevelControl,
  // ...
  )
}

See Supported device types and Trait Index on Android to see all supported device types and traits.

Uncomment Steps 4.1.1 and 4.1.2 in the HomeApp.kt source file to enable the source code that requests the permission.

companion object {
// List of supported device types by this app:
val supportedTypes: List<DeviceTypeFactory<out DeviceType>> = listOf(
// TODO: 4.1.1 - Non-registered device types will be unsupported
//             ContactSensorDevice,
//             ColorTemperatureLightDevice,
//             DimmableLightDevice,
//             ExtendedColorLightDevice,
//             GenericSwitchDevice,
//             GoogleDisplayDevice,
//             GoogleTVDevice,
//             OccupancySensorDevice,
//             OnOffLightDevice,
//             OnOffLightSwitchDevice,
//             OnOffPluginUnitDevice,
//             OnOffSensorDevice,
//             RootNodeDevice,
//             SpeakerDevice,
//             ThermostatDevice,
)
// List of supported device traits by this app:
val supportedTraits: List<TraitFactory<out Trait>> = listOf(
// TODO: 4.1.2 - Non-registered traits will be unsupported
//             AreaAttendanceState,
//             AreaPresenceState,
//             Assistant,
//             AssistantBroadcast,
//             AssistantFulfillment,
//             BasicInformation,
//             BooleanState,
//             OccupancySensing,
//             OnOff,
//             Notification,
//             LevelControl,
//             TemperatureControl,
//             TemperatureMeasurement,
//             Thermostat,
//             Time,
//             Volume,
        )
}

Initialize the HomeClient Object

All apps using the Home APIs initialize a HomeClient object, which is the main interface to interact with the APIs. We prepare this object in the initializer of the HomeApp (HomeApp.kt) class.

// Registry to record device types and traits used in this app:
val registry = FactoryRegistry(
  types = supportedTypes,
  traits = supportedTraits
)
// Configuration options for the HomeClient:
val config = HomeConfig(
  coroutineContext = Dispatchers.IO,
  factoryRegistry = registry
)
// Initialize the HomeClient, which is the primary object to use all Home APIs:
homeClient = Home.getClient(context = context, homeConfig = config)

First, we create a FactoryRegistry using the supported types and traits we defined earlier. Then, using this registry, we initialize a HomeConfig, which contains the configuration needed to run the APIs. Next we use the Home.getClient(...) call to acquire the HomeClient instance.

Our interactions with the Home APIs will all be through this HomeClient object.

Use the Permissions API

User authentication for the Home APIs is done through the Permissions API. The Sample App's PermissionsManager.kt source file contains code for user authentation. Uncomment the contents of the checkPermissions(...) and requestPermissions(...) functions to enable the permissions for the Sample App.

Registering:

homeClient.registerActivityResultCallerForPermissions(activity)

Launching:

try {
    val result: PermissionsResult
    result = homeClient.requestPermissions(forceLaunch = true)
    when (result.status) {
        PermissionsResultStatus.SUCCESS -> // Success Case
        PermissionsResultStatus.CANCELLED -> // User Cancelled
        PermissionsResultStatus.ERROR -> // Some Error
else -> // Unsupported Case
    }
}
catch (e: HomeException) { ... }

Checking:

try {
    val state: PermissionsState
    state = homeClient.hasPermissions().first { state ->
        state != PermissionsState.PERMISSIONS_STATE_UNINITIALIZED
    }
    when (state) {
        PermissionsState.GRANTED -> // Signed In
        PermissionsState.NOT_GRANTED -> // Not Signed In
        PermissionsState.PERMISSIONS_STATE_UNAVAILABLE -> // ...
        PermissionsState.PERMISSIONS_STATE_UNINITIALIZED -> // ...
else -> // Unsupported case
    }
}
catch (e: HomeException) { ... }

Subscribing:

       homeClient.hasPermissions().collect( { state ->
// Track the changes on state
        } )

Uncomment Step 4.3.1 in PermissionsManager.kt to enable the code that requests the permissions:

fun requestPermissions() {
    scope.launch {
    try {
// TODO: 4.3.1 - Request the permissions from the Permissions API
//                 // Request permissions from the Permissions API and record the result:
//                 val result: PermissionsResult = client.requestPermissions(forceLaunch = true)
//                 // Adjust the sign-in status according to permission result:
//                 if (result.status == PermissionsResultStatus.SUCCESS)
//                     isSignedIn.emit(true)
//                 // Report the permission result:
//                 reportPermissionResult(result)
    }
    catch (e: HomeException) { MainActivity.showError(this, e.message.toString()) }
    }
}

Now run the app on your phone, following the steps and allow the permissions. You should see the following flow:

c263dcee4e945bf1.png f518cfd1fdb8a9d8.png 59937372f28c472f.png 383073ae57d2ced4.png 89f774a2ba6898ae.png

The "Loading" message never goes away, but this is because we haven't implemented the code that reads the structure and devices. We'll do that in the next section.

5. Understand the data model

In the Home APIs, the Data Model is composed of:

  • Structure represents a home that contains rooms and devices.
  • Room is a part of a structure and contains devices.
  • Devices (defined as HomeDevice) can be assigned to a structure (or home) or a room in structure.
  • Devices are composed of one or more DeviceType instances.
  • DeviceType is composed of Trait instances.
  • Trait is composed of Attribute instances (for reading/writing), Command instances (for controlling attributes), and Event instances (for reading or subscribing records of past changes).
  • Automation instances are part of a structure and use home metadata and devices to automate tasks in the home.

76d35b44d5a8035e.png

In this section, you'll learn how to develop the source code to show how to use the structure API to parse and render your home structures, rooms, devices, and so forth.

Read structures

The Home APIs design is based on Kotlin Flows to stream out the data model objects (for example, Structure, HomeDevice, and so forth). Developers subscribe to a Flow to get all the objects contained in the object (for example, a Structure, a Room, and so forth).

To retrieve all the structures, call the structures() function, which returns a flow of structures. Then, call the list function on the flow to get all the structures that the user owns.

// Get the a snapshot of all structures from the current homeClient
val allStructures : Set<Structure> =
    homeClient.structures()   // HomeObjectsFlow<Structure>
    .list()                   // Set<Structure>

The Guide to app architecture strongly recommends adopting a modern Reactive programming approach to improve app data flow and state management.

Here's how the Sample App adheres to the Reactive coding style:

  • View models (like StructureViewModel and DeviceViewModel, as the state holder) subscribe to the flows from the Home APIs SDK to receive value changes, and maintain the latest states.
  • Views (like StructureView and DeviceView) subscribe to view models to receive the states and render the UI to reflect those changes.
  • When a user clicks a button on a view (for example, the "On" button of a light device), events trigger the view model's functions, which call the responding Home APIs functions (for example, the OnOff trait's On command).

In Step 5.1.1 in HomeAppViewModel.kt, we subscribe to structure change events by calling the collect() function. Uncomment the section that traverses the structureSet returned by the Structures API response and delivered in the StructureViewModel's StateFlow. This allows the app to monitor structure state changes:

   private suspend fun subscribeToStructures() {
// TODO: 5.1.1 - Subscribe the structure data changes
// // Subscribe to structures returned by the Structures API:
// homeApp.homeClient.structures().collect { structureSet ->
//     val structureVMList: MutableList<StructureViewModel> = mutableListOf()
//     // Store structures in container ViewModels:
//     for (structure in structureSet) {
//         structureVMList.add(StructureViewModel(structure))
//     }
//     // Store the ViewModels:
//     structureVMs.emit(structureVMList)
//
//     // If a structure isn't selected yet, select the first structure from the list:
//     if (selectedStructureVM.value == null && structureVMList.isNotEmpty())
//         selectedStructureVM.emit(structureVMList.first())
//
// }
}

In DevicesView.kt, the app subscribes to the StructureViewModel'sStateFlow, which triggers UI recomposition when structure data changes. Uncomment the source code in Step 5.1.2 to render the structure list as a drop-down menu:

   val structureVMs: List<StructureViewModel> = homeAppVM.structureVMs.collectAsState().value
...
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
// TODO: 5.1.2 - Show list of structures in DropdownMenu
//  for (structure in structureVMs) {
//      DropdownMenuItem(
//          text = { Text(structure.name) },
//          onClick = {
//              scope.launch { homeAppVM.selectedStructureVM.emit(structure) }
//              expanded = false
//          }
//      )
//  }
}
...

Run the app again. You should see the menu when you tap the arrow:

f1fc2be1cb6436b6.png

Parse the Structure

The next step is to traverse the home objects in a structure. Retrieve the rooms from the structure:

val rooms: Set<Room>
rooms = structure.rooms().list()

You can then traverse the rooms to retrieve devices:

val devices: Set<HomeDevice>
devices = room.devices().list()

Important: In the Home APIs data model, a structure can contain devices that aren't assigned to a room, so be sure to capture the devices without rooms in your app as well:

val devicesWithoutRooms: MutableSet<HomeDevice> = mutableSetOf()

for (device in structure.devices().list())
if (!device.isInRoom)
  devicesWithoutRooms.add(device)

Again, in the existing sample code, we subscribe to a flow to get the latest Room and Device list. Check the code at Steps 5.2.1 and 5.2.2 in the StructureViewModel.kt source file and uncomment it to enable the room data subscription:

val roomVMs : MutableStateFlow<List<RoomViewModel>>
val deviceVMs : MutableStateFlow<List<DeviceViewModel>>
val deviceVMsWithoutRooms : MutableStateFlow<List<DeviceViewModel>>
private suspend fun subscribeToRooms() {
// TODO: 5.2.1 - Subscribe the room data changes
//   // Subscribe to changes on rooms:
//   structure.rooms().collect { roomSet ->
//       val roomVMs = mutableListOf<RoomViewModel>()
//       // Store rooms in container ViewModels:
//       for (room in roomSet) {
//           roomVMs.add(RoomViewModel(room))
//       }
//       // Store the ViewModels:
//       this.roomVMs.emit(roomVMs)
//   }
}
private suspend fun subscribeToDevices() {
// TODO: 5.2.2 - Subscribe the device data changes in a structure
//   // Subscribe to changes on devices:
//   structure.devices().collect { deviceSet ->
//       val deviceVMs = mutableListOf<DeviceViewModel>()
//       val deviceWithoutRoomVMs = mutableListOf<DeviceViewModel>()
//       // Store devices in container ViewModels:
//       for (device in deviceSet) {
//           val deviceVM = DeviceViewModel(device)
//           deviceVMs.add(deviceVM)
//           // For any device that's not in a room, additionally keep track of a separate list:
//           if (!device.isInRoom)
//               deviceWithoutRoomVMs.add(deviceVM)
//       }
//       // Store the ViewModels:
//       this.deviceVMs.emit(deviceVMs)
//       deviceVMsWithoutRooms.emit(deviceWithoutRoomVMs)
//   }
    }

Uncomment Steps 5.2.3 and 5.2.4 in the DevicesView.kt source file to render the room list as a menu:

val selectedRoomVMs: List<RoomViewModel> =
selectedStructureVM.roomVMs.collectAsState().value
...
for (roomVM in selectedRoomVMs) {
// TODO: 5.2.3 - Render the list of rooms
//   RoomListItem(roomVM)
// TODO: 5.2.4 - Render the list of devices in a room
//   val deviceVMsInRoom: List<DeviceViewModel> = roomVM.deviceVMs.collectAsState().value
//
//   for (deviceVM in deviceVMsInRoom) {
//       DeviceListItem(deviceVM, homeAppVM)
//   }
}

Now that you have the devices, we'll learn how to work with them.

e715ddda50e04839.png

6. Work with Devices

The Home APIs use a HomeDevice object to capture the device and its capabilities. Developers can subscribe to device attributes and use them to represent smart home devices in their apps.

Read device states

The HomeDevice object presents a set of static values, such as the device name or the connectivity state. As a developer, you can retrieve these soon after you get the device from the APIs:

val id: String = device.id.id
val name: String = device.name
val connectivity: ConnectivityState =
    device.sourceConnectivity.connectivityState

In order to get the device capabilities, you need to retrieve the types and traits from the HomeDevice. To do this, you can subscribe to the device type flow as follows, and retrieve the traits from the device types:

device.types().collect { typeSet ->
var primaryType : DeviceType = UnknownDeviceType()
for (typeInSet in typeSet)
if (typeInSet.metadata.isPrimaryType)
                    primaryType = typeInSet
            val traits: List<Trait> = mutableListOf()
for (trait in primaryType.traits())
if (trait.factory in myTraits)
                    traits.add(trait)
for (trait in traits)
                parseTrait(trait, primaryType)
        }

Each device contains a set of supported DeviceType (bundled capabilities), which you can retrieve using device.types(). These device types contain traits that can be retrieved using type.traits(). Every device marks one of its types as the primary type (which can be checked using type.metadata.isPrimaryType) that you should represent in your app. To provide a complete experience to users, we recommend traversing all returned types and integrating all traits available to you.

Once you retrieve a trait, you can parse it using a function like the following to interpret the values:

fun <T : Trait?> parseTrait(trait : T, type: DeviceType) {
    val status : String = when (trait) {
        is OnOff -> { if (trait.onOff) "On" else "Off" }
        is LevelControl -> { trait.currentLevel.toString() }
        is BooleanState -> {
            when (type.factory) {
                ContactSensorDevice -> {
if (trait.stateValue) "Closed"
else "Open"
                }
else -> ...
            }
        }
else -> ...
    }
}

Note that there can be variations in what a trait represents, depending on the device type featuring it (see BooleanState in the preceding example), so you need to be aware of the context of each device type to understand what their traits really represent.

Uncomment Steps 6.1.1 and 6.1.2 in the DeviceViewModel.kt source file to retrieve the states:

private suspend fun subscribeToType() {
// Subscribe to changes on device type, and the traits/attributes within:
device.types().collect { typeSet ->
// Container for the primary type for this device:
var primaryType : DeviceType = UnknownDeviceType()
...
// TODO: 6.1.1 - Determine the primary type for this device
//       // Among all the types returned for this device, find the primary one:
//       for (typeInSet in typeSet)
//           if (typeInSet.metadata.isPrimaryType)
//               primaryType = typeInSet
//
//       // Optional: For devices with a single type that did not define a primary:
//       if (primaryType is UnknownDeviceType && typeSet.size == 1)
//           primaryType = typeSet.first()
// Container for list of supported traits present on the primary device type:
val supportedTraits: List<Trait> = getSupportedTraits(primaryType.traits())
...
}
fun getSupportedTraits(traits: Set<Trait>) : List<Trait> {
           val supportedTraits: MutableList<Trait> = mutableListOf()
// TODO: 6.1.2 - Get only the supported traits for this device
//   for (trait in traits)
//       if (trait.factory in HomeApp.supportedTraits)
//           supportedTraits.add(trait)
return supportedTraits
}

Uncomment Step 6.1.3 in DeviceView.kt to render an OnOff trait, including its name and status, as a String:

Box (Modifier.padding(horizontal = 24.dp, vertical = 8.dp)) {
when (trait) {
is OnOff -> {
// TODO: 6.1.3 - Render controls based on the trait type
// Column (Modifier.fillMaxWidth()) {
//     Text(trait.factory.toString(), fontSize = 20.sp)
//     Text(DeviceViewModel.getTraitStatus(trait, type), fontSize = 16.sp)
// }
...
}
is LevelControl -> {
      ...
  }
   is BooleanState -> {
      ...
  }
   is OccupancySensing -> {
      ...
  }
  ...
}

If you run the app now with supported device types (for example a Light device), it should show the up-to-date states for all the devices.

1bd8b3b2796c4c7a.png

Issue device commands

To issue commands to devices, the Home APIs provide convenience functions on Trait objects such as trait.on() or trait.moveToLevel(...):

fun <T : Trait?> issueCommand(trait : T) {
     when (trait) {
         is OnOff -> {
// trait.on()
// trait.off()
   }
   is LevelControl -> {
// trait.moveToLevel(...)
// trait.moveToLevelWithOnOff(...)
        }
    }
}

Tip: Once you determine the type of the trait, use the Android Studio's auto-complete feature to see what kind of actions are available for interacting with the trait.

Uncomment Step 6.2.1 in DeviceView.kt to add functional controls in the app:

Box (Modifier.padding(horizontal = 24.dp, vertical = 8.dp)) {
when (trait) {
is OnOff -> {
                ....
// TODO: 6.2.1 - Render controls based on the trait type
//   Switch (checked = (trait.onOff == true), modifier = Modifier.align(Alignment.CenterEnd),
//       onCheckedChange = { state ->
//           scope.launch { if (state) trait.on() else trait.off() }
//       },
//       enabled = isConnected
//   )
}

If you run the app now, it should allow you to control real-life physical devices.

If you tap the OnOff control on your lightbulb, the device should now come on.

c8ed3ecf5031546e.png

For more information about how to control devices, see Control devices on Android.

7. Commission devices

The Commissioning API lets developers add devices to the Google Home ecosystem, and make them available to control using the Home APIs. Only Matter devices are supported. In this section we will explore how you can enable device commissioning in your apps.

Before starting this section, make sure the following prerequisites are met:

If you have a physical Matter device with a QR code for commissioning, you can skip ahead to Enable commissioning API. Otherwise, continue on to the next section, where we discuss how you can use the Matter Virtual Device app (MVD) to create commissionable virtual devices.

Optional: Prepare a Matter commissionable device

The simplest way to prepare a Matter commissionable device is using an emulated device provided by the Matter Virtual Device app (MVD).

After installing the MVD and setting up the firewall, run the MVD:

b20283893073ac1b.png

Create an OnOff device. Notice that it hasn't been commissioned yet - you'll commission it later in this codelab.

5f4855b808312898.png

Enable the Commissioning API

The Commissioning API works outside of the app's Activity, so commissioning needs to be handled differently than the other Home APIs. In order to get your app ready for commissioning, you need two variables.

One variable is ActivityResultLauncher, which is used to send the commissioning intent and to manage the result callback. The other variable is CommissioningResult, which is the object used to store the commissioning result. See the following example for how to set up commissioning:

var launcher: ActivityResultLauncher<IntentSenderRequest>
lateinit var commissioningResult: CommissioningResult?
launcher = activity.registerForActivityResult(StartIntentSenderForResult()) { result ->
try {
  commissioningResult = CommissioningResult.fromIntentSenderResult(
      result.resultCode, result.data)
  } catch (exception: ApiException) {
// Catch any issues
 }
}

Once your commissioning flow is set up, you'll build your commissioning intent, and launch it using the launcher we created in the preceding example. We recommend placing the intent and the launcher in a dedicated function like the following. A dedicated function can be tied to a UI element (such as an +Add Device button) and invoked based on the user request:

fun requestCommissioning() {
// Retrieve the onboarding payload used when commissioning devices:
val payload = activity.intent?.getStringExtra(Matter.EXTRA_ONBOARDING_PAYLOAD)
  scope.launch {
    // Create a commissioning request to store the device in Google's Fabric:
    val request = CommissioningRequest.builder()
      .setStoreToGoogleFabric(true)
      .setOnboardingPayload(payload)
      .build()
    // Initialize client and sender for commissioning intent:
    val client: CommissioningClient = Matter.getCommissioningClient(context)
    val sender: IntentSender = client.commissionDevice(request).await()
    // Launch the commissioning intent on the launcher:
    launcher.launch(IntentSenderRequest.Builder(sender).build())
  }
}

Uncomment Step 7.1.1 in CommissioningManager.kt to enable the commissioning capability and make the +Add Device button work in the Sample App.

// Called by +Add Device button in DeviceView.kt
fun requestCommissioning() {
// Retrieve the onboarding payload used when commissioning devices:
val payload = activity.intent?.getStringExtra(Matter.EXTRA_ONBOARDING_PAYLOAD)
// TODO: 7.1.1 - Launch the commissioning intent
// scope.launch {
//     // Create a commissioning request to store the device in Google's Fabric:
//     val request = CommissioningRequest.builder()
//         .setStoreToGoogleFabric(true)
//         .setOnboardingPayload(payload)
//         .build()
//     // Initialize client and sender for commissioning intent:
//     val client: CommissioningClient = Matter.getCommissioningClient(context)
//     val sender: IntentSender = client.commissionDevice(request).await()
//     // Launch the commissioning intent on the launcher:
//     launcher.launch(IntentSenderRequest.Builder(sender).build())
// }
}

Running this function should start the Commissioning Flow, which should display a screen resembling the following screenshot:

baae45588f460664.png

Understand the Commissioning flow

The commissioning flow includes a set of screens that guides the user through adding a device to their Google Account:

2fb0404820d4a035.png 3cbfa8ff9cfd5ee4.png a177c197ee7a67bf.png 3fdef24672c77c0.png dec8e599f9aa119.png

The users will be greeted with a QR code scanner that they can use to scan the QR codes from Matter devices. The flow will then go through displaying the User Agreement, device discovery and commissioning, and naming the device. Once the flow is complete, the flow will change focus back to the app, and pass the commissioning result in the callback function we drafted in the preceding section.

One benefit of the Commissioning APIs is that the UX flow is handled by the SDK, so developers can get up and running very quickly. This also gives the users a consistent experience when adding devices across different apps.

To know more about the commissioning API, visit Commissioning API on Android.

8. Congratulations!

Congratulations! You've successfully created an Android app using the Google Home APIs. Throughout this codelab, you explored Permissions, Devices, Structures, and Commissioning APIs. In the next codelab, Create advanced automations using the Home APIs on Android Codelab, we will explore the Automation and Discovery APIs, and complete the app.

We hope you enjoy building apps that creatively control devices within the Google Home ecosystem.

Next steps