Criar um app para dispositivos móveis usando as APIs Home no Android

1. Antes de começar

As APIs do Google Home oferecem um conjunto de bibliotecas para que os desenvolvedores Android aproveitem o ecossistema do Google Home. Com essas novas APIs, os desenvolvedores podem criar apps que comissionam e controlam dispositivos de casa inteligente sem problemas.

O Google oferece um app de amostra do Android para desenvolvedores que querem acessar um exemplo funcional usando as APIs Google Home. Este codelab é baseado em uma ramificação do app de exemplo que mostra como usar as APIs Permissions, Commissioning, Device e Structure.

Pré-requisitos

O que você vai aprender

  • Como criar um app Android usando as APIs do Google Home com práticas recomendadas.
  • Como usar as APIs Device e Structure para representar e controlar uma casa inteligente.
  • Como usar as APIs de provisionamento para adicionar dispositivos ao ecossistema do Google Home.

Opcional: configurar sua casa

Antes de usar as APIs do Google Home, você precisa configurar uma casa na sua Conta do Google usando o app Google Home e adicionar alguns dispositivos. Esta seção explica como fazer isso usando o Google Home Playground, que oferece dispositivos virtuais de casa inteligente.

Abra home-playground.withgoogle.com no navegador da Web, faça login com sua Conta do Google e verifique se os seguintes dispositivos emulados aparecem:

  • outlet1: plugue On/Off
  • light2: luz dimerizável
  • light3: luz de ligar/desligar
  • ac3: ar-condicionado
  • blinds4: Cortina
  • washer5: Smart washer

914d23a42b72df8f.png

Abra o app Google Home no dispositivo móvel, toque no botão Adicionar e selecione Compatível com o Google Home. Pesquise "playground" na lista, selecione o projeto "Google Home Playground" e toque em Continuar.

e9ec257b0d9b1ed2.png29fd7416e274d216.pngd974573af0611fd8.png

O Google Home Playground vai mostrar uma página de autorização da conta. Toque em Autorizar ou Fazer login com o Google. Todos os dispositivos configurados no web app vão aparecer no app para dispositivos móveis.

13108a3a15440151.png8791a6d33748f7c8.png

Selecione todos os dispositivos e conclua o processo de configuração. Ao voltar para a página inicial, você vai ver todos os dispositivos disponíveis.

2b021202e6fd1750.png

Os dispositivos compatíveis na lista agora podem ser usados com as APIs Google Home.

2. Criar o projeto

O diagrama a seguir ilustra a arquitetura de um app das APIs Home:

Arquitetura das APIs Home para um app Android

  • Código do app:o código principal em que os desenvolvedores trabalham para criar a interface do usuário do app e a lógica de interação com o SDK das APIs Home.
  • SDK das APIs Home:o SDK das APIs Home fornecido pelo Google funciona com o serviço das APIs Home no GMSCore para controlar dispositivos de casa inteligente. Os desenvolvedores criam apps que funcionam com as APIs Home agrupando-as com o SDK das APIs Home.
  • GMSCore no Android:o GMSCore, também conhecido como Google Play Services, é uma plataforma do Google que oferece serviços principais do sistema, permitindo funcionalidades importantes em todos os dispositivos Android certificados. O módulo inicial do Google Play Services contém os serviços que interagem com as APIs Home.

Configurar o SDK Home

Siga as etapas descritas em Configurar o SDK para acessar a versão mais recente.

Baixar o app de exemplo

O código-fonte do app de exemplo está disponível no GitHub. Este codelab usa os exemplos da ramificação codelab-branch-1 do app de exemplo.

Navegue até o local onde você quer salvar o projeto e clone a ramificação codelab-branch-1:

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

Criar o app de amostra

Siga as etapas de 1 a 5 em Criar o app.

32f2b3c0cd80fcf1.png

Quando o app estiver sendo executado corretamente no smartphone, a página principal do app de exemplo vai aparecer. No entanto, não será possível fazer login até que você configure a autenticação OAuth e implemente as partes ausentes usando a API Permission.

3. Configurar a autenticação

As APIs Home usam o OAuth 2.0 para conceder acesso aos dispositivos na estrutura. O OAuth permite que um usuário conceda permissão a um app ou serviço sem precisar expor as credenciais de login.

Siga as instruções em Configurar o consentimento do OAuth para configurar a tela de consentimento. Crie pelo menos uma conta de teste.

Em seguida, siga as instruções em Configurar credenciais do OAuth para criar as credenciais do app.

4. Inicialização e tratamento de permissões

Nesta seção, você vai aprender a inicializar o SDK e processar permissões de usuário concluindo as partes ausentes usando a API Permissions.

Definir tipos e características compatíveis

Ao desenvolver um app, você precisa indicar explicitamente quais tipos e características de dispositivos ele vai oferecer suporte. No app de exemplo, fazemos isso definindo listas estáticas no objeto complementar em HomeApp.kt, que pode ser referenciado em todo o app conforme necessário:

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

Consulte Tipos de dispositivos compatíveis e Índice de traços no Android para conferir todos os tipos de dispositivos e traços compatíveis.

Remova o comentário das etapas 4.1.1 e 4.1.2 no arquivo de origem HomeApp.kt para ativar o código-fonte que solicita a permissão.

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

Inicializar o objeto HomeClient

Todos os apps que usam as APIs Home inicializam um objeto HomeClient, que é a principal interface para interagir com as APIs. Preparamos esse objeto no inicializador da classe 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)

Primeiro, criamos um FactoryRegistry usando os tipos e traços compatíveis que definimos anteriormente. Em seguida, usando esse registro, inicializamos um HomeConfig, que contém a configuração necessária para executar as APIs. Em seguida, usamos a chamada Home.getClient(...) para adquirir a instância HomeClient.

Todas as nossas interações com as APIs Home serão feitas por esse objeto HomeClient.

Usar a API Permissions

A autenticação de usuário para as APIs Home é feita pela API Permissions. O arquivo de origem PermissionsManager.kt do app de exemplo contém código para autenticação de usuários. Remova o comentário do conteúdo das funções checkPermissions(...) e requestPermissions(...) para ativar as permissões do app de exemplo.

Registro:

homeClient.registerActivityResultCallerForPermissions(activity)

Lançamento:

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

Verificando:

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

Assinatura:

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

Remova a marca de comentário da etapa 4.3.1 em PermissionsManager.kt para ativar o código que solicita as permissões:

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

Agora execute o app no seu smartphone, siga as etapas e permita as permissões. Você vai ver o seguinte fluxo:

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

A mensagem "Carregando" nunca desaparece, mas isso acontece porque não implementamos o código que lê a estrutura e os dispositivos. Vamos fazer isso na próxima seção.

5. Entenda o modelo de dados

Nas APIs Home, o modelo de dados é composto por:

  • Structure representa uma casa com ambientes e dispositivos.
  • Room faz parte de uma estrutura e contém dispositivos.
  • Os dispositivos (definidos como HomeDevice) podem ser atribuídos a uma estrutura (ou casa) ou a um ambiente na estrutura.
  • Os dispositivos são compostos por uma ou mais instâncias de DeviceType.
  • DeviceType é composto por instâncias Trait.
  • O Trait é composto por instâncias Attribute (para leitura/gravação), Command (para controlar atributos) e Event (para ler ou assinar registros de mudanças anteriores).
  • As instâncias Automation fazem parte de uma estrutura e usam metadados e dispositivos domésticos para automatizar tarefas em casa.

76d35b44d5a8035e.png

Nesta seção, você vai aprender a desenvolver o código-fonte para mostrar como usar a API Structure para analisar e renderizar suas estruturas domésticas, salas, dispositivos etc.

Ler estruturas

O design das APIs Home é baseado em fluxos Kotlin para transmitir os objetos do modelo de dados (por exemplo, Structure, HomeDevice e assim por diante). Os desenvolvedores se inscrevem em um Flow para receber todos os objetos contidos nele (por exemplo, um Structure, um Room e assim por diante).

Para recuperar todas as estruturas, chame a função structures(), que retorna um fluxo de estruturas. Em seguida, chame a função de lista no fluxo para receber todas as estruturas que o usuário tem.

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

O Guia para a arquitetura de apps recomenda fortemente a adoção de uma abordagem moderna de programação reativa para melhorar o fluxo de dados e o gerenciamento de estado do app.

Veja como o app de exemplo segue o estilo de programação reativa:

  • Os modelos de visualização (como StructureViewModel e DeviceViewModel, como o detentor de estado) se inscrevem nos fluxos do SDK das APIs Home para receber mudanças de valor e manter os estados mais recentes.
  • As visualizações (como StructureView e DeviceView) se inscrevem em modelos de visualização para receber os estados e renderizar a interface do usuário para refletir essas mudanças.
  • Quando um usuário clica em um botão em uma visualização (por exemplo, o botão "Ativado" de um dispositivo de iluminação), os eventos acionam as funções do modelo de visualização, que chamam as funções correspondentes das APIs Home (por exemplo, o comando On do traço OnOff).

Na etapa 5.1.1 em HomeAppViewModel.kt, fazemos a inscrição em eventos de mudança de estrutura chamando a função collect(). Remova o comentário da seção que percorre o structureSet retornado pela resposta da API Structures e entregue no StructureViewModel's StateFlow. Isso permite que o app monitore mudanças no estado da estrutura:

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

Em DevicesView.kt, o app se inscreve no StructureViewModel'sStateFlow,, que aciona a recomposição da interface quando os dados da estrutura mudam. Remova o comentário do código-fonte na etapa 5.1.2 para renderizar a lista de estrutura como um menu suspenso:

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

Execute o app novamente. O menu vai aparecer quando você tocar na seta:

f1fc2be1cb6436b6.png

Analisar a estrutura

A próxima etapa é percorrer os objetos de casa em uma estrutura. Recupere os ambientes da estrutura:

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

Em seguida, você pode percorrer os cômodos para recuperar dispositivos:

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

Importante:no modelo de dados das APIs Home, uma estrutura pode conter dispositivos que não estão atribuídos a um cômodo. Portanto, capture também os dispositivos sem cômodos no seu app:

val devicesWithoutRooms: MutableSet<HomeDevice> = mutableSetOf()

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

No exemplo de código atual, nos inscreveremos em um fluxo para receber a lista mais recente de salas e dispositivos. Confira o código nas etapas 5.2.1 e 5.2.2 no arquivo de origem StructureViewModel.kt e remova o comentário para ativar a assinatura de dados do Room:

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

Remova a marca de comentário das etapas 5.2.3 e 5.2.4 no arquivo de origem DevicesView.kt para renderizar a lista de salas como um 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)
//   }
}

Agora que você tem os dispositivos, vamos aprender a trabalhar com eles.

e715ddda50e04839.png

6. Trabalhar com dispositivos

As APIs Home usam um objeto HomeDevice para capturar o dispositivo e as funcionalidades dele. Os desenvolvedores podem assinar atributos de dispositivos e usá-los para representar dispositivos de casa inteligente nos apps.

Ler estados do dispositivo

O objeto HomeDevice apresenta um conjunto de valores estáticos, como o nome do dispositivo ou o estado de conectividade. Como desenvolvedor, você pode recuperar essas informações logo após receber o dispositivo das APIs:

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

Para acessar os recursos do dispositivo, recupere os tipos e traços do HomeDevice. Para fazer isso, inscreva-se no fluxo de tipo de dispositivo da seguinte maneira e recupere os traços dos tipos de dispositivo:

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 contém um conjunto de DeviceType (recursos agrupados) compatíveis, que podem ser recuperados usando device.types(). Esses tipos de dispositivos contêm características que podem ser recuperadas usando type.traits(). Cada dispositivo marca um dos tipos como principal (que pode ser verificado usando type.metadata.isPrimaryType) que você deve representar no seu app. Para oferecer uma experiência completa aos usuários, recomendamos percorrer todos os tipos retornados e integrar todos os recursos disponíveis.

Depois de recuperar uma característica, é possível analisá-la usando uma função como a seguinte para interpretar os 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 -> ...
    }
}

Observe que pode haver variações no que um traço representa, dependendo do tipo de dispositivo que o apresenta (consulte BooleanState no exemplo anterior). Portanto, é necessário conhecer o contexto de cada tipo de dispositivo para entender o que os traços realmente representam.

Remova a marca de comentário das etapas 6.1.1 e 6.1.2 no arquivo de origem DeviceViewModel.kt para recuperar os 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
}

Remova o comentário da etapa 6.1.3 em DeviceView.kt para renderizar uma característica OnOff, incluindo o nome e o status dela, como um 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 -> {
      ...
  }
  ...
}

Se você executar o app agora com tipos de dispositivos compatíveis (por exemplo, um dispositivo de iluminação), ele vai mostrar os estados atualizados de todos os dispositivos.

1bd8b3b2796c4c7a.png

Emitir comandos do dispositivo

Para emitir comandos aos dispositivos, as APIs Home oferecem funções convenientes em objetos Trait, como trait.on() ou trait.moveToLevel(...):

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

Dica:depois de determinar o tipo de característica, use o recurso de preenchimento automático do Android Studio para conferir quais ações estão disponíveis para interagir com ela.

Remova o comentário da etapa 6.2.1 em DeviceView.kt para adicionar controles funcionais no 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
//   )
}

Se você executar o app agora, ele vai permitir controlar dispositivos físicos reais.

Se você tocar no controle OnOff da lâmpada, o dispositivo vai acender.

c8ed3ecf5031546e.png

Para mais informações sobre como controlar dispositivos, consulte Controlar dispositivos no Android.

7. Configurar dispositivos

A API Commissioning permite que os desenvolvedores adicionem dispositivos ao ecossistema do Google Home e os disponibilizem para controle usando as APIs Home. Somente dispositivos Matter são compatíveis. Nesta seção, vamos mostrar como ativar o provisionamento de dispositivos nos seus apps.

Antes de começar esta seção, verifique se os seguintes pré-requisitos foram atendidos:

Se você tiver um dispositivo físico do Matter com um QR code para provisionamento, pule para Ativar a API de provisionamento. Caso contrário, continue para a próxima seção, em que discutimos como usar o app Dispositivo virtual do Matter (MVD, na sigla em inglês) para criar dispositivos virtuais comissionáveis.

Opcional: preparar um dispositivo comissionável do Matter

A maneira mais simples de preparar um dispositivo compatível com o Matter é usar um dispositivo emulado fornecido pelo app Matter Virtual Device (MVD).

Depois de instalar o MVD e configurar o firewall, execute o MVD:

b20283893073ac1b.png

Crie um dispositivo OnOff. Ele ainda não foi comissionado. Você vai fazer isso mais tarde neste codelab.

5f4855b808312898.png

Ativar a API Commissioning

A API Commissioning funciona fora da atividade do app. Portanto, o provisionamento precisa ser processado de maneira diferente das outras APIs Home. Para preparar o app para o comissionamento, você precisa de duas variáveis.

Uma variável é ActivityResultLauncher, que é usada para enviar a intent de comissionamento e gerenciar o callback de resultado. A outra variável é CommissioningResult, que é o objeto usado para armazenar o resultado da inclusão. Confira o exemplo a seguir para saber como configurar o provisionamento:

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

Depois de configurar o fluxo de inclusão, você vai criar a intent de inclusão e iniciá-la usando o iniciador criado no exemplo anterior. Recomendamos colocar a intent e o iniciador em uma função dedicada, como a seguinte. Uma função dedicada pode ser vinculada a um elemento da interface (como um botão +Adicionar dispositivo) e invocada com base na solicitação do usuário:

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

Remova o comentário da etapa 7.1.1 em CommissioningManager.kt para ativar a capacidade de provisionamento e fazer com que o botão +Adicionar dispositivo funcione no app de exemplo.

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

A execução dessa função inicia o fluxo de provisionamento, que mostra uma tela semelhante à captura de tela a seguir:

baae45588f460664.png

Entender o fluxo de comissionamento

O fluxo de comissionamento inclui um conjunto de telas que orientam o usuário a adicionar um dispositivo à Conta do Google:

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

Os usuários vão encontrar um leitor de QR code que pode ser usado para ler os códigos dos dispositivos Matter. O fluxo vai mostrar o Contrato do usuário, a descoberta e o provisionamento do dispositivo e a atribuição de um nome a ele. Quando o fluxo for concluído, o foco vai voltar para o app e transmitir o resultado da inclusão na função de callback que criamos na seção anterior.

Um benefício das APIs de provisionamento é que o fluxo da UX é processado pelo SDK, para que os desenvolvedores possam começar a trabalhar muito rapidamente. Isso também oferece aos usuários uma experiência consistente ao adicionar dispositivos em diferentes apps.

Para saber mais sobre a API de provisionamento, acesse API de provisionamento no Android.

8. Parabéns!

Parabéns! Você criou um app Android usando as APIs do Google Home. Ao longo deste codelab, você conheceu as APIs Permissions, Devices, Structures e Commissioning. No próximo codelab, Criar automações avançadas usando as APIs Home no codelab do Android, vamos conhecer as APIs Automation e Discovery e concluir o app.

Esperamos que você goste de criar apps que controlam dispositivos de forma criativa no ecossistema do Google Home.

Próximas etapas