Tworzenie aplikacji mobilnej przy użyciu interfejsów API Home na Androidzie

1. Zanim zaczniesz

Interfejsy API Google Home udostępniają zestaw bibliotek, które umożliwiają deweloperom Androida korzystanie z ekosystemu Google Home. Dzięki tym nowym interfejsom API deweloperzy mogą tworzyć aplikacje, które umożliwiają bezproblemowe sterowanie urządzeniami inteligentnego domu.

Google udostępnia przykładową aplikację na Androida dla deweloperów, którzy chcą uzyskać dostęp do działającego przykładu interfejsów API Google Home. Ten moduł praktyczny oparty na gałęzi przykładowej aplikacji pokazuje, jak korzystać z interfejsów API Permissions, Commissioning, Device i Structure.

Wymagania wstępne

Czego się nauczysz

  • Jak tworzyć aplikacje na Androida z wykorzystaniem interfejsów API Google Home zgodnie ze sprawdzonymi metodami.
  • Jak używać interfejsów Device i Structure API do reprezentowania i sterowania inteligentnym domem.
  • Jak używać interfejsów API do konfigurowania, aby dodawać urządzenia do ekosystemu Google Home.

Opcjonalnie: konfigurowanie domu

Zanim zaczniesz korzystać z interfejsów API Google Home, musisz skonfigurować dom na swoim koncie Google za pomocą aplikacji Google Home i dodać kilka urządzeń. W tej sekcji omawiamy, jak to zrobić za pomocą Google Home Playground, który udostępnia wirtualne urządzenia inteligentnego domu.

Otwórz stronę home-playground.withgoogle.com w przeglądarce, zaloguj się na konto Google i sprawdź, czy pojawiają się te emulowane urządzenia:

  • outlet1: wtyczka włącz/wyłącz
  • light2: światło z możliwością przyciemnienia
  • light3: włączanie/wyłączanie światła
  • ac3: klimatyzator
  • blinds4: Zasłony okienne
  • washer5: inteligentna pralka

914d23a42b72df8f.png

Otwórz aplikację Google Home na urządzeniu mobilnym, kliknij przycisk Dodaj i wybierz Współpracuje z Google Home. Na liście wyszukaj „playground” (plac zabaw), wybierz projekt „Google Home Playground” i kliknij Dalej.

e9ec257b0d9b1ed2.png29fd7416e274d216.pngd974573af0611fd8.png

Na stronie Google Home Playground pojawi się strona autoryzacji konta. Kliknij Autoryzuj lub Zaloguj się przez Google. W aplikacji mobilnej zobaczysz wszystkie urządzenia skonfigurowane w aplikacji internetowej.

13108a3a15440151.png8791a6d33748f7c8.png

Wybierz wszystkie urządzenia i przeprowadź proces konfiguracji. Po powrocie na stronę główną zobaczysz wszystkie dostępne urządzenia.

2b021202e6fd1750.png

Obsługiwane urządzenia na liście są teraz dostępne do użycia z interfejsami Google Home API.

2. Konfigurowanie projektu

Ten diagram przedstawia architekturę aplikacji z interfejsami API Home:

Architektura interfejsów Home API w aplikacji na Androida

  • Kod aplikacji: podstawowy kod, nad którym pracują deweloperzy, aby tworzyć interfejs użytkownika aplikacji i logikę interakcji z pakietem SDK interfejsów API Home.
  • Pakiet SDK interfejsów Home: pakiet SDK interfejsów Home dostarczany przez Google współpracuje z usługą interfejsów Home w GMSCore, aby umożliwić sterowanie urządzeniami inteligentnego domu. Deweloperzy tworzą aplikacje współpracujące z interfejsami API Home, pakując je z pakietem SDK interfejsów API Home.
  • GMSCore na Androidzie: GMSCore, czyli Usługi Google Play, to platforma Google, która zapewnia podstawowe usługi systemowe, umożliwiając działanie najważniejszych funkcji na wszystkich certyfikowanych urządzeniach z Androidem. Moduł Home w usługach Google Play zawiera usługi, które współpracują z interfejsami API Home.

Konfigurowanie pakietu SDK Home

Aby pobrać najnowszy pakiet SDK, wykonaj czynności opisane w sekcji Konfigurowanie pakietu SDK.

Pobieranie przykładowej aplikacji

Kod źródłowy przykładowej aplikacji jest dostępny na GitHubie. Ten warsztat programistyczny korzysta z przykładów z gałęzi codelab-branch-1 przykładowej aplikacji.

Przejdź do miejsca, w którym chcesz zapisać projekt, i sklonuj gałąź codelab-branch-1:

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

Tworzenie przykładowej aplikacji

Wykonaj kroki 1–5 w sekcji Tworzenie aplikacji.

32f2b3c0cd80fcf1.png

Gdy aplikacja uruchomi się na telefonie, zobaczysz stronę główną aplikacji Sample App. Nie będziesz jednak mieć możliwości zalogowania się, dopóki nie skonfigurujesz uwierzytelniania OAuth i nie wdrożysz brakujących elementów za pomocą interfejsu Permission API.

3. Konfigurowanie uwierzytelniania

Interfejsy API Home używają protokołu OAuth 2.0 do przyznawania dostępu do urządzeń w ramach struktury. Protokół OAuth umożliwia użytkownikowi udzielenie uprawnień aplikacji lub usłudze bez konieczności ujawniania danych logowania.

Aby skonfigurować ekran zgody, postępuj zgodnie z instrukcjami w artykule Konfigurowanie zgody OAuth. Pamiętaj, aby utworzyć co najmniej 1 konto testowe.

Następnie postępuj zgodnie z instrukcjami w sekcji Konfigurowanie danych logowania OAuth, aby utworzyć dane logowania aplikacji.

4. Inicjowanie i obsługa uprawnień

W tej sekcji dowiesz się, jak zainicjować pakiet SDK i obsługiwać uprawnienia użytkownika, uzupełniając brakujące elementy za pomocą interfejsu Permissions API.

Definiowanie obsługiwanych typów i właściwości

Podczas tworzenia aplikacji musisz wyraźnie określić, jakie typy urządzeń i cechy będą obsługiwane. W przykładowej aplikacji robimy to, definiując listy statyczne w obiekcie towarzyszącym w obiekcie HomeApp.kt, do których można odwoływać się w całej aplikacji w razie potrzeby:

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

Aby zobaczyć wszystkie obsługiwane typy i cechy urządzeń, przejdź do sekcji Obsługiwane typy urządzeń oraz Indeks cech na urządzeniach z Androidem.

Odkomentuj kroki 4.1.1 i 4.1.2 w pliku źródłowym HomeApp.kt, aby włączyć kod źródłowy, który prosi o to uprawnienie.

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

Inicjowanie obiektu HomeClient

Wszystkie aplikacje korzystające z interfejsów Home inicjują obiekt HomeClient, który jest głównym interfejsem do interakcji z interfejsami API. Przygotowujemy ten obiekt w inicjalizatorze klasy 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)

Najpierw tworzymy FactoryRegistry, korzystając z obsługiwanych typów i właściwości zdefiniowanych wcześniej. Następnie za pomocą tego rejestru inicjujemy HomeConfig, który zawiera konfigurację potrzebną do uruchamiania interfejsów API. Następnie używamy wywołania Home.getClient(...), aby uzyskać instancję HomeClient.

Interakcje z interfejsami API Home będą odbywać się za pomocą obiektu HomeClient.

Korzystanie z interfejsu Permissions API

Uwierzytelnianie użytkowników w przypadku interfejsów API Home odbywa się za pomocą interfejsu Permissions API. Plik źródłowy PermissionsManager.kt przykładowej aplikacji zawiera kod uwierzytelniania użytkownika. Odkomentuj zawartość funkcji checkPermissions(...)requestPermissions(...), aby włączyć uprawnienia dla aplikacji Sample App.

Rejestrowanie:

homeClient.registerActivityResultCallerForPermissions(activity)

Uruchamianie:

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

Sprawdzanie:

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

Subskrybowanie:

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

Odkomentuj krok 4.3.1 w PermissionsManager.kt, aby włączyć kod, który prosi o przyznanie uprawnień:

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

Uruchom aplikację na telefonie, wykonując odpowiednie czynności i wybierając uprawnienia. Powinien pojawić się ten proces:

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

Komunikat „Ładowanie” nigdy nie znika, ale jest to spowodowane tym, że nie wdrożyliśmy kodu, który odczytuje strukturę i urządzenia. Zrobimy to w następnej sekcji.

5. Model danych

W interfejsach API Home model danych składa się z tych elementów:

  • Structure reprezentuje dom, który zawiera pomieszczenia i urządzenia.
  • Room jest częścią struktury i zawiera urządzenia.
  • Urządzenia (zdefiniowane jako HomeDevice) można przypisać do struktury (lub domu) lub pokoju w strukturze.
  • Urządzenia składają się z co najmniej 1 instancji DeviceType.
  • DeviceType składa się z instancji Trait.
  • Trait składa się z instancji Attribute (do odczytu/zapisu), Command (do kontrolowania atrybutów) i Event (do odczytu lub subskrybowania rekordów poprzednich zmian).
  • Automation instancje są częścią struktury i korzystają z metadanych domu oraz urządzeń, aby automatyzować zadania w domu.

76d35b44d5a8035e.png

W tej sekcji dowiesz się, jak opracować kod źródłowy, aby pokazać, jak za pomocą interfejsu Structure API analizować i renderować struktury domów, pokoje, urządzenia itp.

Odczytywanie struktur

Interfejsy API Home są zaprojektowane na podstawie Kotlin Flows, aby strumieniowo przesyłać obiekty modelu danych (np. Structure, HomeDevice itp.). Deweloperzy subskrybują Flow, aby uzyskać dostęp do wszystkich obiektów zawartych w tym obiekcie (np. Structure, Room itd.).

Aby pobrać wszystkie struktury, wywołaj funkcję structures(), która zwraca strumień struktur. Następnie wywołaj funkcję listy w przepływie, aby uzyskać wszystkie struktury należące do użytkownika.

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

Przewodnik po architekturze aplikacji zdecydowanie zaleca stosowanie nowoczesnego podejścia do programowania reaktywnego w celu poprawy przepływu danych i zarządzania stanem aplikacji.

Przykładowa aplikacja przestrzega stylu kodowania Reactive:

  • Modele widoku (takie jak StructureViewModelDeviceViewModel, które są właścicielami stanu) subskrybują przepływy z pakietu SDK interfejsów API Home, aby otrzymywać zmiany wartości i utrzymywać najnowsze stany.
  • Widoki (takie jak StructureViewDeviceView) subskrybują modele widoków, aby otrzymywać stany i renderować interfejs w sposób odzwierciedlający te zmiany.
  • Gdy użytkownik kliknie przycisk w widoku (np. przycisk „Włącz” na urządzeniu sterującym oświetleniem), zdarzenia aktywują funkcje modelu widoku, które wywołują odpowiednie funkcje interfejsów API Home (np. polecenie On atrybutu OnOff).

W kroku 5.1.1 w funkcji HomeAppViewModel.kt subskrybujemy zdarzenia zmiany struktury, wywołując funkcję collect(). Odkomentuj sekcję, która przemierza structureSet zwracany przez interfejs Structures API i dostarczany w StructureViewModel's StateFlow. Pozwala to aplikacji na monitorowanie zmian stanu struktury:

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

DevicesView.kt aplikacja subskrybuje zdarzenie StructureViewModel'sStateFlow,, które powoduje przekształcenie interfejsu użytkownika, gdy zmienią się dane struktury. Odkomentuj kod źródłowy w kroku 5.1.2, aby renderować listę struktury jako menu:

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

Uruchom aplikację ponownie. Po kliknięciu strzałki powinno się wyświetlić menu:

f1fc2be1cb6436b6.png

Przeanalizuj strukturę.

Kolejnym krokiem jest przejście przez obiekty domów w strukturze. Pobieranie pokoi z struktury:

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

Następnie możesz przejść przez pokoje, aby pobrać urządzenia:

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

Ważne: w ramach modelu danych interfejsów API Home struktura może zawierać urządzenia, które nie są przypisane do pokoju. Dlatego w aplikacji uwzględnij też urządzenia bez pokoi:

val devicesWithoutRooms: MutableSet<HomeDevice> = mutableSetOf()

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

W tym przykładowym kodzie również subskrybujemy przepływ danych, aby uzyskać najnowszą listę sal i urządzeń. Sprawdź kod w kroku 5.2.1 i 5.2.2 w pliku źródłowym StructureViewModel.kt i odkomentuj go, aby włączyć subskrypcję danych pokoju:

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

Odkomentuj kroki 5.2.3 i 5.2.4 w pliku źródłowym DevicesView.kt, aby renderować listę sal jako 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)
//   }
}

Teraz, gdy masz już urządzenia, dowiesz się, jak z nimi pracować.

e715ddda50e04839.png

6. Praca z urządzeniami

Interfejsy API Home używają obiektu HomeDevice do rejestrowania urządzenia i jego możliwości. Deweloperzy mogą subskrybować atrybuty urządzenia i wykorzystywać je do reprezentowania inteligentnych urządzeń domowych w swoich aplikacjach.

Czytanie stanów urządzeń

Obiekt HomeDevice zawiera zestaw wartości stałych, takich jak nazwa urządzenia lub stan połączenia. Jako deweloper możesz pobrać te dane zaraz po uzyskaniu dostępu do urządzenia za pomocą interfejsów API:

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

Aby uzyskać informacje o możliwościach urządzenia, musisz pobrać typy i cechy z HomeDevice. Aby to zrobić, możesz w ten sposób zasubskrybować przepływ danych typu urządzenia i pobrać z niego cechy:

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

Każde urządzenie zawiera zestaw obsługiwanych funkcji DeviceType (funkcji w pakiecie), które możesz pobrać za pomocą device.types(). Te typy urządzeń zawierają cechy, które można pobrać za pomocą funkcji type.traits(). Każde urządzenie oznacza jeden z swoich typów jako podstawowy (można to sprawdzić za pomocą funkcji type.metadata.isPrimaryType), który powinieneś reprezentować w aplikacji. Aby zapewnić użytkownikom pełne wrażenia, zalecamy przejrzenie wszystkich zwróconych typów i zintegrowanie wszystkich dostępnych cech.

Po pobraniu cechy możesz ją przeanalizować, używając funkcji takiej jak ta:

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

Pamiętaj, że w zależności od typu urządzenia, na którym występuje dana cecha (patrz BooleanState w poprzednim przykładzie), jej znaczenie może się różnić. Aby zrozumieć, co tak naprawdę oznaczają cechy danego typu urządzenia, musisz znać kontekst.

Odkomentuj kroki 6.1.1 i 6.1.2 w pliku źródłowym DeviceViewModel.kt, aby pobrać stany:

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
}

Odkomentuj krok 6.1.3 w pliku DeviceView.kt, aby renderować cechę OnOff, w tym jej nazwę i stan, jako 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 -> {
      ...
  }
  ...
}

Jeśli uruchomisz aplikację na obsługiwanych typach urządzeń (np. urządzeniu Light), powinna ona wyświetlać aktualne stany wszystkich urządzeń.

1bd8b3b2796c4c7a.png

Wydawanie poleceń na urządzeniu

Aby wydawać polecenia urządzeniom, interfejsy Home API udostępniają funkcje ułatwiające pracę z obiektmi Trait, takimi jak trait.on() lub trait.moveToLevel(...):

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

Wskazówka: gdy określisz typ cechy, użyj funkcji automatycznego uzupełniania w Android Studio, aby sprawdzić, jakie działania są dostępne w powiązaniu z tą cechą.

Odkomentuj krok 6.2.1 w DeviceView.kt, aby dodać elementy sterujące w aplikacji:

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

Jeśli uruchomisz aplikację teraz, powinna ona umożliwić Ci sterowanie rzeczywistymi urządzeniami.

Jeśli klikniesz przycisk włączania/wyłączania na lampie, urządzenie powinno się włączyć.

c8ed3ecf5031546e.png

Więcej informacji o sterowaniu urządzeniami znajdziesz w artykule Sterowanie urządzeniami z Androidem.

7. Konfigurowanie urządzeń

Interfejs Commissioning API umożliwia deweloperom dodawanie urządzeń do ekosystemu Google Home i uzyskiwanie nad nimi kontroli za pomocą interfejsów Home API. Obsługiwane są tylko urządzenia Matter. W tej sekcji omówimy, jak włączyć w swoich aplikacjach konfigurowanie urządzeń.

Zanim przejdziesz do tej sekcji, upewnij się, że są spełnione te wymagania wstępne:

Jeśli masz fizyczne urządzenie obsługujące standard Matter z kodem QR do wdrożenia, możesz przejść do sekcji Włączanie interfejsu API do wdrożenia. W przeciwnym razie przejdź do następnej sekcji, w której omawiamy, jak za pomocą aplikacji Matter Virtual Device (MVD) tworzyć urządzenia wirtualne z możliwością skonfigurowania.

Opcjonalnie: przygotowanie urządzenia z możliwością skonfigurowania Matter

Najprostszym sposobem przygotowania urządzenia do skonfigurowania w Matter jest użycie emulowanego urządzenia udostępnianego przez aplikację Matter Virtual Device (MVD).

Po zainstalowaniu MVD i skonfigurowaniu zapory uruchom MVD:

b20283893073ac1b.png

Utwórz urządzenie typu OnOff. Pamiętaj, że nie został jeszcze uruchomiony – zrobisz to później w ramach tego Codelab.

5f4855b808312898.png

Włączanie interfejsu Commissioning API

Interfejs Commissioning API działa poza aktywnością aplikacji, więc zlecanie musi być obsługiwane inaczej niż inne interfejsy API Home. Aby przygotować aplikację do wdrożenia, musisz ustawić 2 zmiennych.

Jedną z nich jest ActivityResultLauncher, która służy do wysyłania intencji zlecenia i zarządzania wywołaniem zwrotnym wyniku. Drugą zmienną jest CommissioningResult, czyli obiekt służący do przechowywania wyniku zlecenia. Aby dowiedzieć się, jak skonfigurować uruchamianie, zapoznaj się z tym przykładem:

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

Po skonfigurowaniu procesu zlecania utworzysz intencję zlecenia i uruchomisz ją za pomocą narzędzia do uruchamiania, które zostało utworzone w poprzednim przykładzie. Zalecamy umieszczenie intencji i uruchomiciela w funkcji specjalnej, takiej jak ta. Specjalną funkcję można powiązać z elementem interfejsu użytkownika (np. przyciskiem +Dodaj urządzenie) i wywoływać na podstawie żądania użytkownika:

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

Odkomentuj krok 7.1.1 w CommissioningManager.kt, aby włączyć funkcję uruchamiania i uaktywnić przycisk +Dodaj urządzenie w próbnej aplikacji.

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

Uruchomienie tej funkcji powoduje uruchomienie procesu konfiguracji, w ramach którego powinien wyświetlić się ekran podobny do tego na poniższym zrzucie ekranu:

baae45588f460664.png

Omówienie procesu uruchamiania

Proces konfiguracji obejmuje zestaw ekranów, które pomagają użytkownikowi dodać urządzenie do konta Google:

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

Użytkownicy zobaczą skaner kodów QR, za pomocą którego mogą zeskanować kody QR z urządzeń Matter. Następnie wyświetli się Umowa użytkownika, a następnie nastąpi wykrycie i włączenie urządzenia oraz nadanie mu nazwy. Po zakończeniu procesu wróci on do aplikacji i przekaże wynik procesu uruchamiania do funkcji wywołania zwrotnego, którą zaprojektowaliśmy w poprzedniej sekcji.

Jedną z zalet interfejsów API do zlecania zadań jest to, że proces interakcji z użytkownikiem jest obsługiwany przez pakiet SDK, dzięki czemu deweloperzy mogą bardzo szybko rozpocząć pracę. Dzięki temu użytkownicy mogą też dodawać urządzenia w różnych aplikacjach w sposób spójny.

Więcej informacji o interfejsie Commissioning API znajdziesz w artykule Commissioning API na Androidzie.

8. Gratulacje!

Gratulacje! Udało Ci się utworzyć aplikację na Androida za pomocą interfejsów API Google Home. W tym ćwiczeniu z programowania poznasz interfejsy API dotyczące uprawnień, urządzeń, struktur i uruchomiania. W następnym ćwiczeniu z programowania Tworzenie zaawansowanych automatyzacji za pomocą interfejsów API Home na Androidzie w Codelab poznasz interfejsy API automatyzacji i wyszukiwania oraz dokończysz aplikację.

Mamy nadzieję, że tworzenie aplikacji, które umożliwiają kreatywne sterowanie urządzeniami w ekosystemie Google Home, przyniesie Ci wiele satysfakcji.

Dalsze kroki