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
- Znajomość ekosystemu Google Home (chmura–chmura i Matter).
- Stacja robocza z zainstalowaną aplikacją Android Studio (2024.3.1 Ladybug lub nowszą).
- Telefon z Androidem, który spełnia wymagania interfejsów API Home (patrz Wymagania wstępne), z zainstalowanymi Usługami Google Play i aplikacją Google Home.
- zgodny Google Home Hub, który obsługuje interfejsy Google Home API;
- Opcjonalnie: inteligentne urządzenie domowe zgodne z interfejsami Google Home API.
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
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.
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.
Wybierz wszystkie urządzenia i przeprowadź proces konfiguracji. Po powrocie na stronę główną zobaczysz wszystkie dostępne urządzenia.
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:
- 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.
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(...)
i 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:
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 instancjiTrait
.Trait
składa się z instancjiAttribute
(do odczytu/zapisu),Command
(do kontrolowania atrybutów) iEvent
(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.
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
StructureViewModel
iDeviceViewModel
, 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
StructureView
iDeviceView
) 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
atrybutuOnOff
).
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())
//
// }
}
W 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:
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ć.
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ń.
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ć.
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:
- Google Home obsługujący Matter, który znajduje się w tej samej sieci co Twój telefon z Androidem, został dodany do aplikacji Google Home.
- Utworzyłeś/Utworzyłaś projekt dewelopera w Konsoli programisty Google Home o identyfikatorze VID
0xFFF1
i PID0x8000
.
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:
Utwórz urządzenie typu OnOff. Pamiętaj, że nie został jeszcze uruchomiony – zrobisz to później w ramach tego Codelab.
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:
Omówienie procesu uruchamiania
Proces konfiguracji obejmuje zestaw ekranów, które pomagają użytkownikowi dodać urządzenie do konta Google:
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
- Przejdź do następnej części nauki interfejsów Home API na Androidzie, wykonując drugi kurs z tej serii: Tworzenie zaawansowanych automatyzacji za pomocą interfejsów Home API na Androidzie.
- Możesz się z nami skontaktować, aby uzyskać rekomendacje lub zgłosić problemy, korzystając z narzędzia do śledzenia problemów w temacie pomocy dotyczącej inteligentnego domu.