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

1. Zanim zaczniesz

Interfejsy Google Home API to zestaw bibliotek, które umożliwiają deweloperom aplikacji na Androida korzystanie z ekosystemu Google Home. Dzięki tym nowym interfejsom API deweloperzy mogą tworzyć aplikacje, które umożliwiają bezproblemowe konfigurowanie inteligentnych urządzeń domowych i sterowanie nimi.

Google udostępnia przykładową aplikację na Androida dla deweloperów, którzy chcą uzyskać dostęp do działającego przykładu korzystającego z interfejsów Google Home API. Te ćwiczenia z programowania są oparte na gałęzi aplikacji przykładowej, która pokazuje, jak korzystać z interfejsów API uprawnień, uruchamiania, urządzenia i struktury.

Wymagania wstępne

Czego się nauczysz

  • Jak tworzyć aplikacje na Androida za pomocą interfejsów Google Home zgodnie ze sprawdzonymi metodami.
  • Jak używać interfejsów Device i Structure API do reprezentowania inteligentnego domu i sterowania nim.
  • Jak używać interfejsów Commissioning API do dodawania urządzeń do ekosystemu Google Home.

Opcjonalnie: konfigurowanie domu

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

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

  • gniazdko1: włącznik/wyłącznik
  • light2: ściemniane światło
  • light3: włączanie i wyłączanie światła
  • ac3: Klimatyzator
  • blinds4: Window Covering
  • washer5: inteligentna pralka

914d23a42b72df8f.png

Otwórz aplikację Google Home na urządzeniu mobilnym, kliknij przycisk Dodaj i wybierz Działa z Google Home. Na liście wyszukaj „playground”, a następnie wybierz projekt „Google Home Playground” i kliknij Dalej.

e9ec257b0d9b1ed2.png29fd7416e274d216.pngd974573af0611fd8.png

W 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 dokończ proces konfiguracji. Po powrocie na stronę główną zobaczysz wszystkie dostępne urządzenia.

2b021202e6fd1750.png

Urządzenia z tej listy są teraz dostępne do używania z interfejsami Google Home API.

2. Konfigurowanie projektu

Ten diagram ilustruje architekturę aplikacji korzystającej z interfejsów Home API:

Architektura interfejsów Home API w aplikacji na Androida

  • Kod aplikacji: podstawowy kod, nad którym pracują deweloperzy, aby stworzyć interfejs użytkownika aplikacji i logikę interakcji z pakietem SDK interfejsów API Home.
  • Pakiet SDK interfejsów Home API: pakiet SDK interfejsów Home API udostępniany przez Google współpracuje z usługą Home API w GMSCore, aby sterować urządzeniami inteligentnego domu. Deweloperzy tworzą aplikacje współpracujące z interfejsami Home API, łącząc je z pakietem SDK interfejsów Home API.
  • 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ł domowy usług Google Play zawiera usługi, które wchodzą w interakcję z interfejsami Home API.

Konfigurowanie pakietu Home SDK

Aby uzyskać 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. W tym samouczku wykorzystano przykłady z gałęzi codelab-branch-1 aplikacji przykładowej.

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 opisane w sekcji Tworzenie aplikacji.

32f2b3c0cd80fcf1.png

Gdy aplikacja będzie działać prawidłowo na telefonie, zobaczysz stronę główną aplikacji przykładowej. Nie będziesz jednak w stanie się zalogować, dopóki nie skonfigurujesz uwierzytelniania OAuth i nie zaimplementujesz brakujących elementów za pomocą interfejsu Permissions API.

3. Konfigurowanie uwierzytelniania

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

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

Następnie postępuj zgodnie z instrukcjami w artykule 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 zarządzać uprawnieniami użytkowników, uzupełniając brakujące elementy za pomocą interfejsu Permissions API.

Określanie obsługiwanych typów i cech

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

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

Więcej informacji o obsługiwanych typach urządzeń i cechach znajdziesz w sekcjach Obsługiwane typy urządzeńIndeks cech na Androidzie.

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

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 API inicjują obiekt HomeClient, który jest głównym interfejsem do interakcji z tymi interfejsami. Przygotowujemy ten obiekt w inicjatorze 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, używając obsługiwanych typów i cech, które zdefiniowaliśmy wcześniej. Następnie za pomocą tego rejestru inicjujemy obiekt HomeConfig, który zawiera konfigurację potrzebną do uruchomienia interfejsów API. Następnie używamy wywołania Home.getClient(...), aby uzyskać instancję HomeClient.

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

Korzystanie z interfejsu Permissions API

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

Rejestracja:

homeClient.registerActivityResultCallerForPermissions(activity)

Uruchomienie:

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 uprawnienia:

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, wykonaj podane czynności i przyznaj uprawnienia. Powinien pojawić się ten proces:

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

Komunikat „Wczytywanie” nigdy nie znika, ale dzieje się tak, ponieważ nie wdrożyliśmy kodu, który odczytuje strukturę i urządzenia. Zrobimy to w następnej sekcji.

5. Omówienie modelu danych

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

  • Structure reprezentuje dom, w którym znajdują się 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) albo do pomieszczenia w strukturze.
  • Urządzenia składają się z co najmniej 1 DeviceTypeinstancji.
  • DeviceType składa się z Trait instancji.
  • Trait składa się z Attribute instancji (do odczytu i zapisu), Command instancji (do kontrolowania atrybutów) i Event instancji (do odczytywania lub subskrybowania rekordów dotyczących zmian w przeszłości).
  • Instancje Automation są częścią struktury i używają metadanych domu oraz urządzeń do automatyzacji zadań w domu.

76d35b44d5a8035e.png

Z tej sekcji dowiesz się, jak opracować kod źródłowy, aby pokazać, jak używać interfejsu Structure API do analizowania i renderowania struktur domowych, pomieszczeń, urządzeń itp.

Odczytywanie struktur

Interfejsy Home API są oparte na Kotlin Flows, aby przesyłać strumieniowo obiekty modelu danych (np. Structure, HomeDevice itp.). Deweloperzy subskrybują Flow, aby otrzymywać wszystkie obiekty zawarte w obiekcie (np. Structure, Room itp.).

Aby pobrać wszystkie struktury, wywołaj funkcję structures(), która zwraca przepływ struktur. Następnie wywołaj funkcję listy w przepływie, aby uzyskać wszystkie struktury, których właścicielem jest użytkownik.

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

przewodniku po architekturze aplikacji zdecydowanie zalecamy przyjęcie nowoczesnego podejścia do programowania reaktywnego, aby usprawnić przepływ danych w aplikacji i zarządzanie stanem.

Oto jak aplikacja przykładowa jest zgodna ze stylem kodowania reaktywnego:

  • Modele widoku (np. StructureViewModelDeviceViewModel jako elementy przechowujące stan) subskrybują przepływy z pakietu SDK interfejsów API Home, aby otrzymywać zmiany wartości i utrzymywać najnowsze stany.
  • Widoki (np. StructureViewDeviceView) subskrybują modele widoków, aby otrzymywać stany i renderować interfejs użytkownika, odzwierciedlając te zmiany.
  • Gdy użytkownik kliknie przycisk w widoku (np. przycisk „Włącz” na urządzeniu oświetleniowym), zdarzenia wywołują funkcje modelu widoku, które z kolei wywołują odpowiednie funkcje interfejsów API Home (np. polecenie On cechy OnOff).

W kroku 5.1.1 w HomeAppViewModel.kt subskrybujemy zdarzenia zmiany struktury, wywołując funkcję collect(). Odkomentuj sekcję, która przechodzi przez element structureSet zwrócony w odpowiedzi interfejsu Structures API i dostarczony w elemencie StructureViewModel's StateFlow. Dzięki temu aplikacja może monitorować zmiany 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 StructureViewModel'sStateFlow,, co powoduje ponowne komponowanie interfejsu, gdy zmieniają się dane struktury. Odkomentuj kod źródłowy w kroku 5.1.2, aby lista struktur była wyświetlana 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 ponownie aplikację. Po kliknięciu strzałki powinno pojawić się menu:

f1fc2be1cb6436b6.png

Analizowanie struktury

Następnym krokiem jest przechodzenie przez obiekty domowe w strukturze. Pobierz pomieszczenia ze struktury:

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

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

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

Ważne: w modelu danych interfejsów Home API struktura może zawierać urządzenia, które nie są przypisane do pomieszczenia. Pamiętaj, aby w aplikacji uwzględniać też urządzenia bez pomieszczeń:

val devicesWithoutRooms: MutableSet<HomeDevice> = mutableSetOf()

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

W przykładowym kodzie subskrybujemy przepływ, aby uzyskać najnowszą listę pomieszczeń i urządzeń. Sprawdź kod w krokach 5.2.1 i 5.2.2 w StructureViewModel.ktpliku źródłowym i usuń komentarz, aby włączyć subskrypcję danych o pomieszczeniu:

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

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

Gdy już masz urządzenia, dowiedz się, jak z nich korzystać.

e715ddda50e04839.png

6. Praca z urządzeniami

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

Odczytywanie stanów urządzeń

Obiekt HomeDevice zawiera zestaw wartości statycznych, takich jak nazwa urządzenia czy stan połączenia. Jako deweloper możesz pobrać te informacje z interfejsów API zaraz po otrzymaniu urządzenia:

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 zasubskrybować przepływ typów urządzeń w ten sposób i pobrać cechy z typów urządzeń:

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 DeviceType (zintegrowanych funkcji), 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 typów jako typ podstawowy (można to sprawdzić za pomocą type.metadata.isPrimaryType), który należy uwzględnić w aplikacji. Aby zapewnić użytkownikom pełną funkcjonalność, zalecamy przechodzenie przez wszystkie zwrócone typy i integrowanie wszystkich dostępnych funkcji.

Po pobraniu cechy możesz ją przeanalizować za pomocą funkcji takiej jak ta poniżej, aby zinterpretować wartości:

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 to, co reprezentuje cecha, może się różnić w zależności od typu urządzenia, na którym występuje (patrz BooleanState w przykładzie powyżej). Musisz więc znać kontekst każdego typu urządzenia, aby zrozumieć, co naprawdę reprezentują jego cechy.

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 DeviceView.kt, aby wyrenderować 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 teraz uruchomisz aplikację na obsługiwanych typach urządzeń (np. na 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ą wygodne funkcje w obiektach cech, takie 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: po określeniu typu cechy użyj funkcji autouzupełniania w Android Studio, aby sprawdzić, jakie działania są dostępne w przypadku interakcji z cechą.

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

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 teraz uruchomisz aplikację, powinna ona umożliwiać sterowanie rzeczywistymi urządzeniami fizycznymi.

Jeśli klikniesz element sterujący włączaniem i wyłączaniem na żarówce, urządzenie powinno się włączyć.

c8ed3ecf5031546e.png

Więcej informacji o sterowaniu urządzeniami znajdziesz w artykule Sterowanie urządzeniami na Androidzie.

7. Konfigurowanie urządzeń

Interfejs Commissioning API umożliwia programistom dodawanie urządzeń do ekosystemu Google Home i udostępnianie ich do sterowania za pomocą interfejsów Home API. Obsługiwane są tylko urządzenia Matter. Z tej sekcji dowiesz się, jak włączyć w aplikacjach wdrażanie urządzeń.

Zanim zaczniesz tę sekcję, upewnij się, że spełniasz te wymagania wstępne:

Jeśli masz fizyczne urządzenie Matter z kodem QR do uruchomienia, możesz przejść do sekcji Włączanie interfejsu Commissioning API. W przeciwnym razie przejdź do następnej sekcji, w której omawiamy, jak za pomocą aplikacji Matter Virtual Device (MVD) tworzyć wirtualne urządzenia, które można wprowadzać do eksploatacji.

Opcjonalnie: przygotuj urządzenie obsługujące standard Matter

Najprostszym sposobem przygotowania urządzenia zgodnego z 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 OnOff. Zwróć uwagę, że nie został jeszcze uruchomiony – zrobisz to później w tym laboratorium.

5f4855b808312898.png

Włączanie interfejsu Commissioning API

Interfejs Commissioning API działa poza aktywnością aplikacji, więc proces uruchamiania musi być obsługiwany inaczej niż w przypadku innych interfejsów Home API. Aby przygotować aplikację do uruchomienia, potrzebujesz 2 zmiennych.

Jedną ze zmiennych jest ActivityResultLauncher, która służy do wysyłania intencji uruchomienia i zarządzania wywołaniem zwrotnym wyniku. Drugą zmienną jest CommissioningResult, czyli obiekt używany do przechowywania wyniku uruchomienia. Poniżej znajdziesz przykład konfiguracji uruchomienia:

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 uruchamiania utworzysz intencję uruchamiania i uruchomisz ją za pomocą programu uruchamiającego utworzonego w poprzednim przykładzie. Zalecamy umieszczenie intencji i programu uruchamiającego w odpowiedniej funkcji, takiej jak ta poniżej. Funkcję można powiązać z elementem interfejsu (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ć możliwość uruchomienia i sprawić, że w aplikacji próbnej będzie działać przycisk +Add Device (Dodaj urządzenie).

// 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 powinno rozpocząć proces wprowadzania urządzenia do eksploatacji, który powinien wyświetlić ekran podobny do tego na poniższym zrzucie ekranu:

baae45588f460664.png

Omówienie procesu uruchamiania

Proces uruchamiania obejmuje zestaw ekranów, które prowadzą użytkownika przez dodawanie urządzenia do konta Google:

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

Użytkownicy zobaczą skaner kodów QR, którego mogą użyć do skanowania kodów QR z urządzeń Matter. Następnie wyświetli się Umowa z użytkownikiem, a potem nastąpi wykrywanie i uruchamianie urządzenia oraz nadawanie mu nazwy. Po zakończeniu procesu fokus zostanie przeniesiony z powrotem do aplikacji, a wynik uruchomienia zostanie przekazany w funkcji wywołania zwrotnego, którą utworzyliśmy w poprzedniej sekcji.

Jedną z zalet interfejsów API do uruchamiania jest to, że przepływ UX jest obsługiwany przez pakiet SDK, więc deweloperzy mogą bardzo szybko rozpocząć pracę. Zapewnia to też użytkownikom spójne wrażenia podczas dodawania urządzeń w różnych aplikacjach.

Więcej informacji o interfejsie API do uruchamiania znajdziesz w artykule Interfejs API do uruchamiania na Androidzie.

8. Gratulacje!

Gratulacje! Udało Ci się utworzyć aplikację na Androida za pomocą interfejsów Google Home API. W tym ćwiczeniu z programowania zapoznaliśmy się z interfejsami API uprawnień, urządzeń, struktur i wdrażania. W kolejnych ćwiczeniach z programowania Tworzenie zaawansowanych automatyzacji za pomocą interfejsów Home API na Androidzie przyjrzymy się interfejsom Automation API i Discovery API oraz dokończymy aplikację.

Mamy nadzieję, że tworzenie aplikacji, które w kreatywny sposób sterują urządzeniami w ekosystemie Google Home, będzie dla Ciebie przyjemnością.

Dalsze kroki