Compila una app para dispositivos móviles con las APIs de Home en Android

1. Antes de comenzar

Las APIs de Google Home proporcionan un conjunto de bibliotecas para que los desarrolladores de Android aprovechen el ecosistema de Google Home. Con estas nuevas APIs, los desarrolladores pueden compilar apps que pongan en funcionamiento y controlen dispositivos inteligentes para la casa sin problemas.

Google proporciona una app de ejemplo para Android para los desarrolladores que desean acceder a un ejemplo funcional con las APIs de Google Home. Este codelab se basa en una rama de la app de ejemplo que te guía por el uso de las APIs de Permissions, Commissioning, Device y Structure.

Requisitos previos

Qué aprenderás

  • Cómo compilar una app para Android con las APIs de Google Home y las prácticas recomendadas
  • Cómo usar las APIs de Device y Structure para representar y controlar una casa inteligente
  • Cómo usar las APIs de Commissioning para agregar dispositivos al ecosistema de Google Home

Opcional: Configura tu casa

Antes de usar las APIs de Google Home, deberás configurar una casa en tu Cuenta de Google con la app de Google Home y agregar algunos dispositivos. En esta sección, se explica cómo hacerlo con Google Home Playground, que proporciona dispositivos inteligentes para la casa virtuales.

Abre home-playground.withgoogle.com en tu navegador web, accede con tu Cuenta de Google y verifica si aparecen los siguientes dispositivos emulados:

  • outlet1: Enchufe de encendido/apagado
  • light2: Luz regulable
  • light3: Luz de encendido/apagado
  • ac3: Aire acondicionado
  • blinds4: Window Covering
  • washer5: Lavadora inteligente

914d23a42b72df8f.png

Abre la app de Google Home en tu dispositivo móvil, presiona el botón Agregar y selecciona Funciona con Google Home. Busca "playground" en la lista, selecciona el proyecto "Google Home Playground" y presiona Continuar.

e9ec257b0d9b1ed2.png29fd7416e274d216.pngd974573af0611fd8.png

La zona de pruebas de Google Home te mostrará una página de autorización de la cuenta. Presiona Autorizar o Acceder con Google. Verás todos los dispositivos que configuraste desde la app web en la app para dispositivos móviles.

13108a3a15440151.png8791a6d33748f7c8.png

Selecciona todos los dispositivos y completa el proceso de configuración. Cuando vuelvas a la página principal, verás todos los dispositivos disponibles.

2b021202e6fd1750.png

Los dispositivos compatibles de la lista ahora están disponibles para usarse con las APIs de Google Home.

2. Configura tu proyecto

En el siguiente diagrama, se ilustra la arquitectura de una app de las APIs de Home:

Arquitectura de las APIs de Home para una app para Android

  • Código de la app: Es el código principal en el que trabajan los desarrolladores para compilar la interfaz de usuario de la app y la lógica para interactuar con el SDK de las APIs de Home.
  • SDK de Home APIs: El SDK de Home APIs que proporciona Google funciona con el servicio de Home APIs en GMSCore para controlar dispositivos para la casa inteligente. Los desarrolladores compilan apps que funcionan con las APIs de Home incluyéndolas en el SDK de las APIs de Home.
  • GMSCore en Android: GMSCore, también conocido como Servicios de Google Play, es una plataforma de Google que proporciona servicios del sistema principales y habilita funciones clave en todos los dispositivos Android certificados. El módulo principal de los Servicios de Google Play contiene los servicios que interactúan con las APIs de Home.

Configura el SDK de Home

Sigue los pasos que se describen en Configura el SDK para obtener el SDK más reciente.

Obtén la app de ejemplo

El código fuente de la app de ejemplo está disponible en GitHub. En este codelab, se usan los ejemplos de la rama codelab-branch-1 de la app de ejemplo.

Navega hasta donde quieras guardar el proyecto y clona la rama codelab-branch-1:

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

Compila la app de ejemplo

Realiza los pasos del 1 al 5 en Compila la app.

32f2b3c0cd80fcf1.png

Cuando la app se ejecute correctamente en tu teléfono, verás la página principal de la app de ejemplo. Sin embargo, no podrás acceder hasta que configures la autenticación OAuth y, luego, implementes las partes faltantes con la API de Permissions.

3. Configura la autenticación

Las APIs de Home usan OAuth 2.0 para otorgar acceso a los dispositivos de la estructura. OAuth permite que un usuario otorgue permiso a una app o un servicio sin tener que exponer sus credenciales de acceso.

Sigue las instrucciones en Configura el consentimiento de OAuth para configurar la pantalla de consentimiento. Asegúrate de crear al menos una cuenta de prueba.

Luego, sigue las instrucciones que se indican en Configura credenciales de OAuth para crear las credenciales de la app.

4. Inicialización y control de permisos

En esta sección, aprenderás a inicializar el SDK y a controlar los permisos del usuario completando las partes faltantes con la API de Permissions.

Define los tipos y rasgos admitidos

Cuando desarrolles una app, deberás indicar explícitamente qué tipos y características de dispositivos admitirá. En la app de ejemplo, definimos listas estáticas en el objeto complementario en HomeApp.kt, al que luego se puede hacer referencia en toda la app según sea necesario:

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,
  // ...
  )
}

Consulta Tipos de dispositivos compatibles y el Índice de rasgos en Android para ver todos los tipos de dispositivos y rasgos compatibles.

Quita las marcas de comentario de los pasos 4.1.1 y 4.1.2 en el archivo fuente HomeApp.kt para habilitar el código fuente que solicita el permiso.

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,
        )
}

Inicializa el objeto HomeClient

Todas las apps que usan las APIs de Home inicializan un objeto HomeClient, que es la interfaz principal para interactuar con las APIs. Preparamos este objeto en el inicializador de la clase HomeApp (HomeApp.kt).

// 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)

Primero, creamos un FactoryRegistry con los tipos y rasgos admitidos que definimos anteriormente. Luego, con este registro, inicializamos un HomeConfig, que contiene la configuración necesaria para ejecutar las APIs. A continuación, usamos la llamada Home.getClient(...) para adquirir la instancia de HomeClient.

Todas nuestras interacciones con las APIs de Home se realizarán a través de este objeto HomeClient.

Usa la API de Permissions

La autenticación de usuarios para las APIs de Home se realiza a través de la API de Permissions. El archivo fuente PermissionsManager.kt de la app de ejemplo contiene código para la autenticación del usuario. Quita las marcas de comentario del contenido de las funciones checkPermissions(...) y requestPermissions(...) para habilitar los permisos de la app de ejemplo.

Registro:

homeClient.registerActivityResultCallerForPermissions(activity)

Lanzamiento:

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) { ... }

Verificación:

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) { ... }

Suscripción:

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

Quita el comentario del paso 4.3.1 en PermissionsManager.kt para habilitar el código que solicita los permisos:

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()) }
    }
}

Ahora ejecuta la app en tu teléfono, sigue los pasos y permite los permisos. Deberías ver el siguiente flujo:

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

El mensaje "Cargando" nunca desaparece, pero esto se debe a que no implementamos el código que lee la estructura y los dispositivos. Lo haremos en la siguiente sección.

5. Obtén información sobre el modelo de datos

En las APIs para la casa, el modelo de datos se compone de los siguientes elementos:

  • Structure representa una casa que contiene habitaciones y dispositivos.
  • Room forma parte de una estructura y contiene dispositivos.
  • Los dispositivos (definidos como HomeDevice) se pueden asignar a una estructura (o casa) o a una habitación de la estructura.
  • Los dispositivos se componen de una o más instancias de DeviceType.
  • DeviceType se compone de instancias Trait.
  • Trait se compone de instancias de Attribute (para lectura y escritura), instancias de Command (para controlar atributos) y instancias de Event (para leer o suscribirse a registros de cambios anteriores).
  • Las instancias de Automation forman parte de una estructura y usan metadatos y dispositivos de la casa para automatizar tareas en ella.

76d35b44d5a8035e.png

En esta sección, aprenderás a desarrollar el código fuente para mostrar cómo usar la API de Structure para analizar y renderizar tus estructuras, habitaciones, dispositivos, etcétera.

Estructuras de lectura

El diseño de las APIs de Home se basa en Kotlin Flows para transmitir los objetos del modelo de datos (por ejemplo, Structure, HomeDevice, etcétera). Los desarrolladores se suscriben a un Flow para obtener todos los objetos que contiene (por ejemplo, un Structure, un Room, etcétera).

Para recuperar todas las estructuras, llama a la función structures(), que devuelve un flujo de estructuras. Luego, llama a la función de lista en el flujo para obtener todas las estructuras que posee el usuario.

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

La Guía de arquitectura de apps recomienda adoptar un enfoque moderno de programación reactiva para mejorar el flujo de datos y la administración de estados de la app.

A continuación, se muestra cómo la app de ejemplo se ajusta al estilo de programación reactiva:

  • Los modelos de vistas (como StructureViewModel y DeviceViewModel, como el titular del estado) se suscriben a los flujos del SDK de las APIs de Home para recibir cambios de valores y mantener los estados más recientes.
  • Las vistas (como StructureView y DeviceView) se suscriben a los modelos de vistas para recibir los estados y renderizar la IU de modo que refleje esos cambios.
  • Cuando un usuario hace clic en un botón de una vista (por ejemplo, el botón "Encender" de un dispositivo de luz), los eventos activan las funciones del modelo de vista, que llaman a las funciones de las APIs de Home correspondientes (por ejemplo, el comando On del rasgo OnOff).

En el paso 5.1.1 de HomeAppViewModel.kt, nos suscribimos a los eventos de cambio de estructura llamando a la función collect(). Quita las marcas de comentario de la sección que recorre el structureSet que muestra la respuesta de la API de Structures y que se entrega en el StructureViewModel's StateFlow. Esto permite que la app supervise los cambios de estado de la estructura:

   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())
//
// }
}

En DevicesView.kt, la app se suscribe a StructureViewModel'sStateFlow,, lo que activa la recomposición de la IU cuando cambian los datos de estructura. Quita la marca de comentario del código fuente en el paso 5.1.2 para renderizar la lista de estructuras como un menú desplegable:

   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
//          }
//      )
//  }
}
...

Vuelve a ejecutar la app. Deberías ver el menú cuando presiones la flecha:

f1fc2be1cb6436b6.png

Analiza la estructura

El siguiente paso es recorrer los objetos de la casa en una estructura. Recupera las habitaciones de la estructura:

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

Luego, puedes recorrer las habitaciones para recuperar dispositivos:

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

Importante: En el modelo de datos de las APIs de Home, una estructura puede contener dispositivos que no están asignados a una habitación, por lo que debes asegurarte de capturar también los dispositivos sin habitaciones en tu app:

val devicesWithoutRooms: MutableSet<HomeDevice> = mutableSetOf()

for (device in structure.devices().list())
if (device.roomId == null)
  devicesWithoutRooms.add(device)

Nuevamente, en el código de muestra existente, nos suscribimos a un flujo para obtener la lista más reciente de habitaciones y dispositivos. Verifica el código en los pasos 5.2.1 y 5.2.2 del archivo fuente StructureViewModel.kt y quita las marcas de comentario para habilitar la suscripción a los datos de la habitación:

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.roomId == null)
//               deviceWithoutRoomVMs.add(deviceVM)
//       }
//       // Store the ViewModels:
//       this.deviceVMs.emit(deviceVMs)
//       deviceVMsWithoutRooms.emit(deviceWithoutRoomVMs)
//   }
    }

Quita los comentarios de los pasos 5.2.3 y 5.2.4 en el archivo fuente DevicesView.kt para renderizar la lista de salas como un menú:

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)
//   }
}

Ahora que tienes los dispositivos, aprenderemos a trabajar con ellos.

e715ddda50e04839.png

6. Cómo trabajar con dispositivos

Las APIs de Home usan un objeto HomeDevice para capturar el dispositivo y sus capacidades. Los desarrolladores pueden suscribirse a los atributos de los dispositivos y usarlos para representar dispositivos inteligentes para la casa en sus apps.

Leer estados de dispositivos

El objeto HomeDevice presenta un conjunto de valores estáticos, como el nombre del dispositivo o el estado de conectividad. Como desarrollador, puedes recuperar estos datos poco después de obtener el dispositivo de las APIs:

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

Para obtener las capacidades del dispositivo, debes recuperar los tipos y los rasgos del HomeDevice. Para ello, puedes suscribirte al flujo del tipo de dispositivo de la siguiente manera y recuperar los rasgos de los tipos de dispositivos:

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)
        }

Cada dispositivo contiene un conjunto de DeviceType compatibles (capacidades incluidas), que puedes recuperar con device.types(). Estos tipos de dispositivos contienen rasgos que se pueden recuperar con type.traits(). Cada dispositivo marca uno de sus tipos como el tipo principal (que se puede verificar con type.metadata.isPrimaryType) que debes representar en tu app. Para brindar una experiencia completa a los usuarios, te recomendamos que recorras todos los tipos que se devuelven y que integres todos los rasgos disponibles para ti.

Una vez que recuperes un rasgo, puedes analizarlo con una función como la siguiente para interpretar los valores:

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 -> ...
    }
}

Ten en cuenta que puede haber variaciones en lo que representa un rasgo, según el tipo de dispositivo que lo incluya (consulta BooleanState en el ejemplo anterior), por lo que debes conocer el contexto de cada tipo de dispositivo para comprender lo que realmente representan sus rasgos.

Quita los comentarios de los pasos 6.1.1 y 6.1.2 en el archivo fuente DeviceViewModel.kt para recuperar los estados:

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
}

Quita la marca de comentario del paso 6.1.3 en DeviceView.kt para renderizar un rasgo OnOff, incluidos su nombre y estado, como un 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 -> {
      ...
  }
  ...
}

Si ejecutas la app ahora con tipos de dispositivos compatibles (por ejemplo, un dispositivo Light), debería mostrar los estados actualizados de todos los dispositivos.

1bd8b3b2796c4c7a.png

Cómo emitir comandos del dispositivo

Para enviar comandos a los dispositivos, las APIs de Home proporcionan funciones convenientes en objetos de rasgos, como trait.on() o trait.moveToLevel(...):

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

Nota: Una vez que determines el tipo de rasgo, usa la función de autocompletar de Android Studio para ver qué tipo de acciones están disponibles para interactuar con el rasgo.

Quita la marca de comentario del paso 6.2.1 en DeviceView.kt para agregar controles funcionales en la 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
//   )
}

Si ejecutas la app ahora, debería permitirte controlar dispositivos físicos de la vida real.

Si presionas el control de encendido y apagado de la bombilla, el dispositivo debería encenderse.

c8ed3ecf5031546e.png

Para obtener más información sobre cómo controlar dispositivos, consulta Cómo controlar dispositivos en Android.

7. Puesta en marcha de dispositivos

La API de Commissioning permite a los desarrolladores agregar dispositivos al ecosistema de Google Home y hacer que estén disponibles para controlarlos con las APIs de Home. Solo se admiten dispositivos Matter. En esta sección, exploraremos cómo puedes habilitar la puesta en servicio de dispositivos en tus apps.

Antes de comenzar esta sección, asegúrate de que se cumplan los siguientes requisitos:

Si tienes un dispositivo Matter físico con un código QR para la puesta en servicio, puedes avanzar a Habilita la API de puesta en servicio. De lo contrario, continúa con la siguiente sección, en la que analizaremos cómo puedes usar la app de Matter Virtual Device (MVD) para crear dispositivos virtuales aptos para la puesta en servicio.

Opcional: Prepara un dispositivo apto para la asignación de Matter

La forma más sencilla de preparar un dispositivo apto para la puesta en servicio de Matter es usar un dispositivo emulado que proporciona la app de Matter Virtual Device (MVD).

Después de instalar el MVD y configurar el firewall, ejecuta el MVD:

b20283893073ac1b.png

Crea un dispositivo OnOff. Observa que aún no se encargó. Lo harás más adelante en este codelab.

5f4855b808312898.png

Habilita la API de Commissioning

La API de Commissioning funciona fuera de la actividad de la app, por lo que la puesta en servicio debe controlarse de manera diferente a las otras APIs de Home. Para preparar tu app para la comisión, necesitas dos variables.

Una variable es ActivityResultLauncher, que se usa para enviar la intención de puesta en servicio y administrar la devolución de llamada del resultado. La otra variable es CommissioningResult, que es el objeto que se usa para almacenar el resultado de la puesta en servicio. Consulta el siguiente ejemplo para saber cómo configurar la puesta en marcha:

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
 }
}

Una vez que configures el flujo de puesta en marcha, compilarás tu intent de puesta en marcha y lo iniciarás con el selector que creamos en el ejemplo anterior. Te recomendamos que coloques el intent y el selector en una función dedicada como la siguiente. Se puede vincular una función dedicada a un elemento de la IU (como un botón +Add Device) y se puede invocar según la solicitud del usuario:

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())
  }
}

Quita la marca de comentario del paso 7.1.1 en CommissioningManager.kt para habilitar la capacidad de puesta en servicio y hacer que funcione el botón +Add Device en la app de ejemplo.

// 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())
// }
}

Si ejecutas esta función, se debería iniciar el flujo de puesta en servicio, que debería mostrar una pantalla similar a la siguiente captura de pantalla:

baae45588f460664.png

Información sobre el flujo de puesta en marcha

El flujo de puesta en marcha incluye un conjunto de pantallas que guían al usuario para agregar un dispositivo a su Cuenta de Google:

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

Los usuarios verán un escáner de códigos QR que podrán usar para escanear los códigos QR de los dispositivos Matter. Luego, el flujo mostrará el Acuerdo del usuario, la detección y la puesta en servicio del dispositivo, y la asignación de un nombre al dispositivo. Una vez que se complete el flujo, el enfoque volverá a la app y pasará el resultado de la puesta en servicio en la función de devolución de llamada que creamos en la sección anterior.

Un beneficio de las APIs de Commissioning es que el SDK controla el flujo de UX, por lo que los desarrolladores pueden comenzar a trabajar muy rápidamente. Esto también les brinda a los usuarios una experiencia coherente cuando agregan dispositivos en diferentes apps.

Para obtener más información sobre la API de Commissioning, visita Commissioning API en Android.

8. ¡Felicitaciones!

¡Felicitaciones! Creaste correctamente una app para Android con las APIs de Google Home. A lo largo de este codelab, exploraste las APIs de Permissions, Devices, Structures y Commissioning. En el próximo codelab, Codelab sobre cómo crear automatizaciones avanzadas con las APIs de Home en Android, exploraremos las APIs de Automation y Discovery, y completaremos la app.

Esperamos que disfrutes crear apps que controlen dispositivos de forma creativa dentro del ecosistema de Google Home.

Próximos pasos