Creare un'app mobile utilizzando le API Home su Android

1. Prima di iniziare

Le API Google Home forniscono un insieme di librerie per consentire agli sviluppatori Android di accedere all'ecosistema Google Home. Con queste nuove API, gli sviluppatori possono creare app che eseguono il provisioning e controllano senza problemi i dispositivi per la smart home.

Google fornisce un'app di esempio per Android per gli sviluppatori che vogliono accedere a un esempio funzionante utilizzando le API Google Home. Questo codelab si basa su un ramo dell'app di esempio che ti guida nell'utilizzo delle API Permissions, Commissioning, Device e Structure.

Prerequisiti

Obiettivi didattici

  • Come creare un'app per Android utilizzando le API Google Home con le best practice.
  • Come utilizzare le API Device e Structure per rappresentare e controllare una smart home.
  • Come utilizzare le API di provisioning per aggiungere dispositivi all'ecosistema Google Home.

(Facoltativo) Configurare la casa

Prima di utilizzare le API Google Home, devi configurare una casa sul tuo Account Google utilizzando l'app Google Home e aggiungere alcuni dispositivi. Questa sezione spiega come farlo utilizzando Google Home Playground, che fornisce dispositivi per la smart home virtuali.

Apri home-playground.withgoogle.com nel browser web, accedi con il tuo Account Google e verifica se vengono visualizzati i seguenti dispositivi emulati:

  • presa1: presa on/off
  • light2: Luce dimmerabile
  • light3: On/Off light
  • ac3: Air conditioner
  • blinds4: Window Covering
  • washer5: Smart washer

914d23a42b72df8f.png

Apri l'app Google Home sul dispositivo mobile, tocca il pulsante Aggiungi e seleziona Compatibile con Google Home. Cerca "playground" nell'elenco, seleziona il progetto "Google Home Playground" e tocca Continua.

e9ec257b0d9b1ed2.png29fd7416e274d216.pngd974573af0611fd8.png

Google Home Playground mostrerà una pagina di autorizzazione dell'account. Tocca Autorizza o Accedi con Google. Vedrai tutti i dispositivi che hai configurato dall'app web nell'app mobile.

13108a3a15440151.png8791a6d33748f7c8.png

Seleziona tutti i dispositivi e completa la procedura di configurazione. Tornando alla home page, vedrai tutti i dispositivi disponibili.

2b021202e6fd1750.png

I dispositivi supportati nell'elenco sono ora disponibili per l'utilizzo con le API Google Home.

2. Configura il progetto

Il seguente diagramma illustra l'architettura di un'app Home APIs:

Architettura delle API Home per un'app per Android

  • Codice app:il codice principale su cui gli sviluppatori lavorano per creare l'interfaccia utente dell'app e la logica per interagire con l'SDK Home APIs.
  • SDK Home APIs:l'SDK Home APIs fornito da Google funziona con il servizio Home APIs in GMSCore per controllare i dispositivi per la smart home. Gli sviluppatori creano app che funzionano con le API Home raggruppandole con l'SDK Home APIs.
  • GMSCore su Android: GMSCore, noto anche come Google Play Services, è una piattaforma Google che fornisce servizi di sistema di base, consentendo funzionalità chiave su tutti i dispositivi Android certificati. Il modulo Home di Google Play Services contiene i servizi che interagiscono con le API Home.

Configura l'SDK Home

Per ottenere l'SDK più recente, segui i passaggi descritti in Configurare l'SDK.

Ottieni l'app di esempio

Il codice sorgente dell'app di esempio è disponibile su GitHub. Questo codelab utilizza gli esempi del ramo codelab-branch-1 dell'app di esempio.

Vai alla posizione in cui vuoi salvare il progetto e clona il ramo codelab-branch-1:

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

Crea l'app di esempio

Esegui i passaggi 1-5 della sezione Crea l'app.

32f2b3c0cd80fcf1.png

Quando l'app viene eseguita correttamente sullo smartphone, viene visualizzata la pagina principale dell'app di esempio. Tuttavia, non potrai accedere finché non configurerai l'autenticazione OAuth e implementerai i componenti mancanti utilizzando l'API Permissions.

3. Configurare l'autenticazione

Le API Home utilizzano OAuth 2.0 per concedere l'accesso ai dispositivi nella struttura. OAuth consente a un utente di concedere l'autorizzazione a un'app o a un servizio senza dover esporre le proprie credenziali di accesso.

Segui le istruzioni riportate in Configurare il consenso OAuth per configurare la schermata per il consenso. Assicurati di creare almeno un account di test.

Poi segui le istruzioni riportate in Configurare le credenziali OAuth per creare le credenziali per l'app.

4. Inizializzazione e gestione delle autorizzazioni

In questa sezione imparerai a inizializzare l'SDK e a gestire le autorizzazioni utente completando le parti mancanti utilizzando l'API Permissions.

Definisci i tipi e i tratti supportati

Quando sviluppi un'app, devi indicare esplicitamente quali tipi di dispositivi e caratteristiche supporterà. Nell'app di esempio, questa operazione viene eseguita definendo elenchi statici nell'oggetto complementare in HomeApp.kt, a cui è possibile fare riferimento in tutta l'app in base alle esigenze:

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 Tipi di dispositivi supportati e Indice delle caratteristiche su Android per visualizzare tutti i tipi di dispositivi e le caratteristiche supportati.

Rimuovi il commento dai passaggi 4.1.1 e 4.1.2 nel file di origine HomeApp.kt per attivare il codice sorgente che richiede l'autorizzazione.

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

Inizializza l'oggetto HomeClient

Tutte le app che utilizzano le API Home inizializzano un oggetto HomeClient, che è l'interfaccia principale per interagire con le API. Prepariamo questo oggetto nell'inizializzatore della 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)

Innanzitutto, creiamo un FactoryRegistry utilizzando i tipi e i tratti supportati che abbiamo definito in precedenza. Poi, utilizzando questo registro, inizializziamo un HomeConfig, che contiene la configurazione necessaria per eseguire le API. Successivamente, utilizziamo la chiamata Home.getClient(...) per acquisire l'istanza HomeClient.

Tutte le nostre interazioni con le API Home avverranno tramite questo oggetto HomeClient.

Utilizzare l'API Permissions

L'autenticazione utente per le API Home viene eseguita tramite l'API Permissions. Il file di origine PermissionsManager.kt dell'app di esempio contiene il codice per l'autenticazione utente. Rimuovi il commento dai contenuti delle funzioni checkPermissions(...) e requestPermissions(...) per attivare le autorizzazioni per l'app di esempio.

Registrazione in corso:

homeClient.registerActivityResultCallerForPermissions(activity)

Lancio:

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

Controllo:

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

Sottoscrizione in corso:

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

Rimuovi il commento dal passaggio 4.3.1 in PermissionsManager.kt per attivare il codice che richiede le autorizzazioni:

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

Ora esegui l'app sullo smartphone, seguendo i passaggi e consentendo le autorizzazioni. Dovresti vedere il seguente flusso:

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

Il messaggio "Caricamento" non scompare mai, ma questo perché non abbiamo implementato il codice che legge la struttura e i dispositivi. Lo faremo nella sezione successiva.

5. Informazioni sul modello dei dati

Nelle API Home, il modello dei dati è composto da:

  • Structure rappresenta una casa che contiene stanze e dispositivi.
  • Room fa parte di una struttura e contiene dispositivi.
  • I dispositivi (definiti come HomeDevice) possono essere assegnati a una struttura (o casa) o a una stanza della struttura.
  • I dispositivi sono composti da una o più istanze DeviceType.
  • DeviceType è composto da Trait istanze.
  • Trait è composto da Attribute istanze (per la lettura/scrittura), Command istanze (per il controllo degli attributi) e Event istanze (per la lettura o la sottoscrizione dei record delle modifiche passate).
  • Le istanze Automation fanno parte di una struttura e utilizzano i metadati e i dispositivi della casa per automatizzare le attività in casa.

76d35b44d5a8035e.png

In questa sezione imparerai a sviluppare il codice sorgente per mostrare come utilizzare l'API Structure per analizzare e visualizzare le strutture della casa, le stanze, i dispositivi e così via.

Strutture di lettura

La progettazione delle API Home si basa su Kotlin Flows per trasmettere in streaming gli oggetti del modello di dati (ad esempio Structure, HomeDevice e così via). Gli sviluppatori si abbonano a un Flow per ottenere tutti gli oggetti contenuti nell'oggetto (ad esempio, un Structure, un Room e così via).

Per recuperare tutte le strutture, chiama la funzione structures(), che restituisce un flusso di strutture. Quindi, chiama la funzione di elenco nel flusso per ottenere tutte le strutture di proprietà dell'utente.

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

La Guida all'architettura delle app consiglia vivamente di adottare un approccio di programmazione reattiva moderno per migliorare il flusso di dati e la gestione dello stato delle app.

Ecco come l'app di esempio rispetta lo stile di programmazione reattiva:

  • Le visualizzazioni (come StructureViewModel e DeviceViewModel, in qualità di titolari dello stato) si iscrivono ai flussi dell'SDK Home APIs per ricevere le modifiche dei valori e mantenere gli stati più recenti.
  • Le visualizzazioni (come StructureView e DeviceView) si abbonano ai modelli di visualizzazione per ricevere gli stati e visualizzare l'interfaccia utente in modo da riflettere queste modifiche.
  • Quando un utente fa clic su un pulsante di una visualizzazione (ad esempio, il pulsante "On" di un dispositivo di illuminazione), gli eventi attivano le funzioni del modello di visualizzazione, che chiamano le funzioni delle API Home corrispondenti (ad esempio, il comando On del tratto OnOff).

Nel passaggio 5.1.1 di HomeAppViewModel.kt, ci abboniamo agli eventi di modifica della struttura chiamando la funzione collect(). Rimuovi il commento dalla sezione che attraversa structureSet restituito dalla risposta dell'API Structures e fornito in StructureViewModel's StateFlow. Ciò consente all'app di monitorare le modifiche allo stato della struttura:

   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, l'app si iscrive a StructureViewModel'sStateFlow,, che attiva la ricomposizione dell'interfaccia utente quando cambiano i dati strutturati. Rimuovi il commento dal codice sorgente nel passaggio 5.1.2 per visualizzare l'elenco della struttura come menu a discesa:

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

Esegui di nuovo l'app. Quando tocchi la freccia, dovresti vedere il menu:

f1fc2be1cb6436b6.png

Analizzare la struttura

Il passaggio successivo consiste nell'attraversare gli oggetti della casa in una struttura. Recupera le stanze dalla struttura:

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

Puoi quindi spostarti tra le stanze per recuperare i dispositivi:

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

Importante:nel modello di dati delle API Home, una struttura può contenere dispositivi non assegnati a una stanza, quindi assicurati di acquisire anche i dispositivi senza stanze nella tua app:

val devicesWithoutRooms: MutableSet<HomeDevice> = mutableSetOf()

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

Anche in questo caso, nel codice campione esistente, ci abboniamo a un flusso per ottenere l'elenco più recente di stanze e dispositivi. Controlla il codice nei passaggi 5.2.1 e 5.2.2 del file di origine StructureViewModel.kt e rimuovi il commento per attivare l'abbonamento ai dati della stanza:

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

Rimuovi il commento dai passaggi 5.2.3 e 5.2.4 nel file di origine DevicesView.kt per visualizzare l'elenco delle stanze come 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)
//   }
}

Ora che hai i dispositivi, impareremo a utilizzarli.

e715ddda50e04839.png

6. Utilizzare i dispositivi

Le API Home utilizzano un oggetto HomeDevice per acquisire il dispositivo e le sue funzionalità. Gli sviluppatori possono abbonarsi agli attributi dei dispositivi e utilizzarli per rappresentare i dispositivi per la smart home nelle loro app.

Lettura degli stati dei dispositivi

L'oggetto HomeDevice presenta un insieme di valori statici, come il nome del dispositivo o lo stato della connettività. In qualità di sviluppatore, puoi recuperare questi dati dalle API subito dopo aver ricevuto il dispositivo:

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

Per ottenere le funzionalità del dispositivo, devi recuperare i tipi e i tratti da HomeDevice. Per farlo, puoi abbonarti al flusso del tipo di dispositivo nel seguente modo e recuperare i tratti dai tipi di 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)
        }

Ogni dispositivo contiene un insieme di DeviceType supportate (funzionalità in bundle), che puoi recuperare utilizzando device.types(). Questi tipi di dispositivi contengono caratteristiche che possono essere recuperate utilizzando type.traits(). Ogni dispositivo contrassegna uno dei suoi tipi come tipo principale (che può essere controllato utilizzando type.metadata.isPrimaryType) che devi rappresentare nella tua app. Per offrire un'esperienza completa agli utenti, ti consigliamo di attraversare tutti i tipi restituiti e integrare tutti i tratti a tua disposizione.

Una volta recuperato un tratto, puoi analizzarlo utilizzando una funzione come la seguente per interpretare i valori:

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

Tieni presente che ciò che rappresenta un tratto può variare a seconda del tipo di dispositivo che lo include (vedi BooleanState nell'esempio precedente), quindi devi conoscere il contesto di ogni tipo di dispositivo per capire cosa rappresentano realmente i suoi tratti.

Rimuovi il commento dai passaggi 6.1.1 e 6.1.2 nel file di origine DeviceViewModel.kt per recuperare gli stati:

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
}

Rimuovi il commento dal passaggio 6.1.3 in DeviceView.kt per eseguire il rendering di un tratto OnOff, inclusi nome e stato, come 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 esegui l'app ora con i tipi di dispositivi supportati (ad esempio un dispositivo di illuminazione), dovresti visualizzare gli stati aggiornati di tutti i dispositivi.

1bd8b3b2796c4c7a.png

Esegui comandi dispositivo

Per inviare comandi ai dispositivi, le API Home forniscono funzioni pratiche sugli oggetti tratto, ad esempio 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(...)
        }
    }
}

Suggerimento:una volta determinato il tipo di tratto, utilizza la funzionalità di completamento automatico di Android Studio per vedere quali tipi di azioni sono disponibili per interagire con il tratto.

Rimuovi il commento dal passaggio 6.2.1 in DeviceView.kt per aggiungere controlli funzionali nell'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 esegui l'app ora, dovrebbe consentirti di controllare dispositivi fisici reali.

Se tocchi il controllo OnOff sulla lampadina, il dispositivo dovrebbe accendersi.

c8ed3ecf5031546e.png

Per saperne di più su come controllare i dispositivi, vedi Controllare i dispositivi su Android.

7. Configurare i dispositivi

L'API Commissioning consente agli sviluppatori di aggiungere dispositivi all'ecosistema Google Home e di renderli disponibili per il controllo tramite le API Home. Sono supportati solo i dispositivi Matter. In questa sezione esploreremo come abilitare il provisioning dei dispositivi nelle tue app.

Prima di iniziare questa sezione, assicurati che siano soddisfatti i seguenti prerequisiti:

Se hai un dispositivo Matter fisico con un codice QR per il provisioning, puoi passare direttamente ad Abilitare l'API di provisioning. In caso contrario, continua con la sezione successiva, in cui viene illustrato come utilizzare l'app Matter Virtual Device (MVD) per creare dispositivi virtuali commissionabili.

(Facoltativo) Prepara un dispositivo commissionabile Matter

Il modo più semplice per preparare un dispositivo commissionabile Matter è utilizzare un dispositivo emulato fornito dall'app Matter Virtual Device (MVD).

Dopo aver installato MVD e configurato il firewall, esegui MVD:

b20283893073ac1b.png

Crea un dispositivo OnOff. Tieni presente che non è ancora stato commissionato. Lo farai più avanti in questo codelab.

5f4855b808312898.png

Abilita l'API Commissioning

L'API Commissioning funziona al di fuori dell'attività dell'app, pertanto il provisioning deve essere gestito in modo diverso rispetto alle altre API Home. Per preparare l'app per la messa in servizio, sono necessarie due variabili.

Una variabile è ActivityResultLauncher, che viene utilizzata per inviare l'intent di provisioning e per gestire il callback dei risultati. L'altra variabile è CommissioningResult, ovvero l'oggetto utilizzato per archiviare il risultato del provisioning. Vedi l'esempio seguente per scoprire come configurare la commissione:

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 volta configurato il flusso di commissioning, creerai l'intent di commissioning e lo avvierai utilizzando il launcher creato nell'esempio precedente. Ti consigliamo di inserire l'intent e l'avvio in una funzione dedicata come la seguente. Una funzione dedicata può essere associata a un elemento dell'interfaccia utente (ad esempio un pulsante +Aggiungi dispositivo) e richiamata in base alla richiesta dell'utente:

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

Rimuovi il commento dal passaggio 7.1.1 in CommissioningManager.kt per attivare la funzionalità di provisioning e far funzionare il pulsante +Aggiungi dispositivo nell'app di esempio.

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

L'esecuzione di questa funzione dovrebbe avviare il flusso di provisioning, che dovrebbe mostrare una schermata simile allo screenshot seguente:

baae45588f460664.png

Informazioni sul flusso di commissioning

Il flusso di provisioning include una serie di schermate che guidano l'utente nell'aggiunta di un dispositivo al proprio Account Google:

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

Gli utenti visualizzeranno uno scanner di codici QR che possono utilizzare per scansionare i codici QR dei dispositivi Matter. Il flusso proseguirà con la visualizzazione del Contratto con l'utente, il rilevamento e il provisioning del dispositivo e l'assegnazione del nome al dispositivo. Al termine del flusso, il focus tornerà sull'app e il risultato della configurazione verrà passato alla funzione di callback che abbiamo creato nella sezione precedente.

Uno dei vantaggi delle API di provisioning è che il flusso UX viene gestito dall'SDK, quindi gli sviluppatori possono iniziare a lavorare molto rapidamente. In questo modo, gli utenti hanno un'esperienza coerente quando aggiungono dispositivi in app diverse.

Per saperne di più sull'API di provisioning, visita la pagina API di provisioning su Android.

8. Complimenti!

Complimenti! Hai creato correttamente un'app per Android utilizzando le API Google Home. Nel corso di questo codelab, hai esplorato le API Permissions, Devices, Structures e Commissioning. Nel prossimo codelab, Create advanced automations using the Home APIs on Android Codelab, esploreremo le API Automation e Discovery e completeremo l'app.

Ci auguriamo che ti divertirai a creare app che controllano in modo creativo i dispositivi all'interno dell'ecosistema Google Home.

Passaggi successivi