1. Sebelum memulai
Ini adalah codelab kedua dalam seri tentang membangun aplikasi Android menggunakan Google Home API. Dalam codelab ini, kita akan membahas cara membuat otomatisasi rumah dan memberikan beberapa tips tentang praktik terbaik penggunaan API. Jika Anda belum menyelesaikan codelab pertama, Membangun aplikasi seluler menggunakan Home API di Android, sebaiknya selesaikan codelab tersebut sebelum memulai codelab ini.
API Google Home menyediakan serangkaian library bagi developer Android untuk mengontrol perangkat smart home dalam ekosistem Google Home. Dengan API baru ini, developer akan dapat menyetel otomatisasi untuk smart home yang dapat mengontrol kemampuan perangkat berdasarkan kondisi yang telah ditentukan. Google juga menyediakan Discovery API yang memungkinkan Anda membuat kueri perangkat untuk mengetahui atribut dan perintah yang didukungnya.
Prasyarat
- Selesaikan codelab Membangun aplikasi seluler menggunakan Home API di Android.
- Pengetahuan tentang ekosistem Google Home (Cloud-to-cloud dan Matter).
- Workstation dengan Android Studio (2024.3.1 Ladybug atau yang lebih baru) terinstal.
- Ponsel Android yang memenuhi persyaratan Home API (lihat Prasyarat), dengan Layanan Google Play dan Aplikasi Google Home yang terinstal. Emulator tidak akan berfungsi, hanya ponsel Android fisik yang didukung untuk aplikasi contoh.
- Google Home Hub yang kompatibel dan mendukung Google Home API.
- Opsional - Perangkat smart home yang kompatibel dengan Google Home API.
Yang akan Anda pelajari
- Cara membuat otomatisasi untuk perangkat smart home menggunakan Home API.
- Cara menggunakan Discovery API untuk menjelajahi kemampuan perangkat yang didukung.
- Cara menerapkan praktik terbaik saat membangun aplikasi Anda dengan Home API.
2. Menyiapkan project
Diagram berikut mengilustrasikan arsitektur aplikasi Home API:
- Kode Aplikasi: Kode inti yang dikerjakan developer untuk membangun antarmuka pengguna aplikasi dan logika untuk berinteraksi dengan Home APIs SDK.
- Home APIs SDK: Home APIs SDK yang disediakan oleh Google berfungsi dengan Layanan Home API di GMSCore untuk mengontrol perangkat smart home. Developer membuat aplikasi yang berfungsi dengan Home API dengan memaketkannya dengan SDK Home API.
- GMSCore di Android: GMSCore, yang juga dikenal sebagai layanan Google Play, adalah platform Google yang menyediakan layanan sistem inti, sehingga mengaktifkan fungsi utama di semua perangkat Android bersertifikasi. Modul beranda layanan Google Play berisi layanan yang berinteraksi dengan Home API.
Dalam codelab ini, kita akan melanjutkan pembahasan yang telah kita bahas dalam Membangun aplikasi seluler menggunakan Home API di Android.
Pastikan Anda memiliki struktur dengan setidaknya dua perangkat yang didukung yang disiapkan dan berfungsi di akun. Karena kita akan menyiapkan otomatisasi dalam codelab ini (perubahan pada status perangkat memicu tindakan di perangkat lain), Anda akan memerlukan dua perangkat untuk melihat hasilnya.
Mendapatkan Aplikasi Contoh
Kode sumber untuk Aplikasi Contoh tersedia di GitHub di repositori google-home/google-home-api-sample-app-android.
Codelab ini menggunakan contoh dari cabang codelab-branch-2
Aplikasi Contoh.
Buka tempat Anda ingin menyimpan project dan clone cabang codelab-branch-2
:
$ git clone -b codelab-branch-2 https://github.com/google-home/google-home-api-sample-app-android.git
Perhatikan bahwa ini adalah cabang yang berbeda dengan yang digunakan dalam Membangun aplikasi seluler menggunakan Home API di Android. Cabang codebase ini dibuat berdasarkan tempat codelab pertama dihentikan. Kali ini, contohnya akan memandu Anda membuat otomatisasi. Jika Anda telah menyelesaikan codelab sebelumnya dan dapat membuat semua fungsi berjalan, Anda dapat memilih untuk menggunakan project Android Studio yang sama untuk menyelesaikan codelab ini, bukan menggunakan codelab-branch-2
.
Setelah Anda mengompilasi kode sumber dan siap dijalankan di perangkat seluler, lanjutkan ke bagian berikutnya.
3. Mempelajari Otomatisasi
Otomatisasi adalah serangkaian pernyataan "jika ini, maka itu" yang dapat mengontrol status perangkat berdasarkan faktor yang dipilih, secara otomatis. Developer dapat menggunakan otomatisasi untuk membuat fitur interaktif lanjutan di API mereka.
Otomatisasi terdiri dari tiga jenis komponen berbeda yang dikenal sebagai node: pemicu, tindakan, dan kondisi. Node ini bekerja sama untuk mengotomatiskan perilaku menggunakan perangkat smart home. Biasanya, mereka dievaluasi dalam urutan berikut:
- Pemicu — Menentukan kondisi awal yang mengaktifkan otomatisasi, seperti perubahan pada nilai karakteristik. Otomatisasi harus memiliki Pemicu.
- Kondisi — Batasan tambahan apa pun yang akan dievaluasi setelah otomatisasi dipicu. Ekspresi dalam Kondisi harus dievaluasi sebagai benar agar tindakan otomatisasi dapat dijalankan.
- Tindakan — Perintah atau pembaruan status yang dilakukan saat semua kondisi telah terpenuhi.
Misalnya, Anda dapat memiliki otomatisasi yang meredupkan lampu di ruangan saat sakelar diaktifkan, sementara TV di ruangan tersebut dinyalakan. Dalam contoh ini:
- Starter — Sakelar di ruangan diaktifkan.
- Kondisi— Status OnOff TV dievaluasi sebagai Aktif.
- Tindakan — Lampu di ruangan yang sama dengan Sakelar akan diredupkan.
Node ini dievaluasi oleh Automation Engine secara berurutan atau paralel.
Sequential Flow berisi node yang dieksekusi secara berurutan. Biasanya, ini adalah pemicu, kondisi, dan tindakan.
Alur Paralel dapat memiliki beberapa node tindakan yang dijalankan secara bersamaan, seperti menyalakan beberapa lampu pada waktu yang sama. Node yang mengikuti alur paralel tidak akan dieksekusi hingga semua cabang alur paralel selesai.
Ada jenis node lain dalam skema otomatisasi. Anda dapat mempelajari lebih lanjut di bagian Node di Panduan Developer Home API. Selain itu, developer dapat menggabungkan berbagai jenis node untuk membuat otomatisasi yang kompleks, seperti berikut:
Developer menyediakan node ini ke Automation Engine menggunakan bahasa khusus domain (DSL) yang dibuat khusus untuk otomatisasi Google Home.
Mempelajari DSL Otomatisasi
Bahasa khusus domain (DSL) adalah bahasa yang digunakan untuk merekam perilaku sistem dalam kode. Compiler menghasilkan class data yang diserialisasi ke JSON buffer protokol, dan digunakan untuk melakukan panggilan ke Layanan Otomatisasi Google.
DSL mencari skema berikut:
automation {
name = "AutomationName"
description = "An example automation description."
isActive = true
sequential {
val onOffTrait = starter<_>(device1, OnOffLightDevice, OnOff)
condition() { expression = onOffTrait.onOff equals true }
action(device2, OnOffLightDevice) { command(OnOff.on()) }
}
}
Otomatisasi dalam contoh sebelumnya menyinkronkan dua bohlam lampu. Saat status OnOff
device1
berubah menjadi On
(onOffTrait.onOff equals true
), status OnOff
device2
diubah menjadi On
(command(OnOff.on()
).
Saat Anda bekerja dengan otomatisasi, ketahui bahwa ada batas resource.
Otomatisasi adalah alat yang sangat berguna untuk membuat kemampuan otomatis di smart home. Dalam kasus penggunaan paling dasar, Anda dapat secara eksplisit mengodekan otomatisasi untuk menggunakan perangkat dan karakteristik tertentu. Namun, kasus penggunaan yang lebih praktis adalah saat aplikasi memungkinkan pengguna mengonfigurasi perangkat, perintah, dan parameter otomatisasi. Bagian berikutnya menjelaskan cara membuat editor otomatisasi yang memungkinkan pengguna melakukan hal tersebut.
4. Membangun editor otomatisasi
Dalam Aplikasi Contoh, kita akan membuat editor otomatisasi yang dapat digunakan pengguna untuk memilih perangkat, kemampuan (tindakan) yang ingin mereka gunakan, dan cara pemicuan otomatisasi menggunakan pemicu.
Menyiapkan pemicu
Pemicu otomatisasi adalah titik entri untuk otomatisasi. Pemicu memulai otomatisasi saat peristiwa tertentu terjadi. Di Aplikasi Contoh, kita merekam pemicu otomatisasi menggunakan class StarterViewModel
, yang ada di file sumber StarterViewModel.kt
, dan menampilkan tampilan editor menggunakan StarterView
(StarterView.kt
).
Node awal memerlukan elemen berikut:
- Perangkat
- Ciri
- Operasi
- Nilai
Perangkat dan karakteristik dapat dipilih dari objek yang ditampilkan oleh Devices API. Perintah dan parameter untuk setiap perangkat yang didukung adalah masalah yang lebih kompleks dan perlu ditangani secara terpisah.
Aplikasi menentukan daftar operasi yang telah ditetapkan sebelumnya:
// List of operations available when creating automation starters:
enum class Operation {
EQUALS,
NOT_EQUALS,
GREATER_THAN,
GREATER_THAN_OR_EQUALS,
LESS_THAN,
LESS_THAN_OR_EQUALS
}
Kemudian, untuk setiap trait yang didukung, operasi yang didukung akan dilacak:
// List of operations available when comparing booleans:
object BooleanOperations : Operations(listOf(
Operation.EQUALS,
Operation.NOT_EQUALS
))
// List of operations available when comparing values:
object LevelOperations : Operations(listOf(
Operation.GREATER_THAN,
Operation.GREATER_THAN_OR_EQUALS,
Operation.LESS_THAN,
Operation.LESS_THAN_OR_EQUALS
))
Dengan cara yang sama, Aplikasi Contoh melacak nilai yang dapat ditetapkan ke sifat:
enum class OnOffValue {
On,
Off,
}
enum class ThermostatValue {
Heat,
Cool,
Off,
}
Dan melacak pemetaan antara nilai yang ditentukan oleh aplikasi dan nilai yang ditentukan oleh API:
val valuesOnOff: Map<OnOffValue, Boolean> = mapOf(
OnOffValue.On to true,
OnOffValue.Off to false,
)
val valuesThermostat: Map<ThermostatValue, ThermostatTrait.SystemModeEnum> = mapOf(
ThermostatValue.Heat to ThermostatTrait.SystemModeEnum.Heat,
ThermostatValue.Cool to ThermostatTrait.SystemModeEnum.Cool,
ThermostatValue.Off to ThermostatTrait.SystemModeEnum.Off,
)
Kemudian, aplikasi akan menampilkan serangkaian elemen tampilan yang dapat digunakan pengguna untuk memilih kolom yang diperlukan.
Hapus tanda komentar Langkah 4.1.1 dalam file StarterView.kt
untuk merender semua perangkat awal dan menerapkan callback klik di DropdownMenu
:
val deviceVMs: List<DeviceViewModel> = structureVM.deviceVMs.collectAsState().value
...
DropdownMenu(expanded = expandedDeviceSelection, onDismissRequest = { expandedDeviceSelection = false }) {
// TODO: 4.1.1 - Starter device selection dropdown
// for (deviceVM in deviceVMs) {
// DropdownMenuItem(
// text = { Text(deviceVM.name) },
// onClick = {
// scope.launch {
// starterDeviceVM.value = deviceVM
// starterType.value = deviceVM.type.value
// starterTrait.value = null
// starterOperation.value = null
// }
// expandedDeviceSelection = false
// }
// )
// }
}
Hapus tanda komentar Langkah 4.1.2 dalam file StarterView.kt
untuk merender semua karakteristik perangkat awal dan menerapkan callback klik di DropdownMenu
:
// Selected starter attributes for StarterView on screen:
val starterDeviceVM: MutableState<DeviceViewModel?> = remember {
mutableStateOf(starterVM.deviceVM.value) }
...
DropdownMenu(expanded = expandedTraitSelection, onDismissRequest = { expandedTraitSelection = false }) {
// TODO: 4.1.2 - Starter device traits selection dropdown
// val deviceTraits = starterDeviceVM.value?.traits?.collectAsState()?.value!!
// for (trait in deviceTraits) {
// DropdownMenuItem(
// text = { Text(trait.factory.toString()) },
// onClick = {
// scope.launch {
// starterTrait.value = trait.factory
// starterOperation.value = null
// }
// expandedTraitSelection = false
// }
// )
}
}
Batalkan komentar Langkah 4.1.3 dalam file StarterView.kt
untuk merender semua operasi trait yang dipilih dan menerapkan callback klik di DropdownMenu
:
val starterOperation: MutableState<StarterViewModel.Operation?> = remember {
mutableStateOf(starterVM.operation.value) }
...
DropdownMenu(expanded = expandedOperationSelection, onDismissRequest = { expandedOperationSelection = false }) {
// ...
if (!StarterViewModel.starterOperations.containsKey(starterTrait.value))
return@DropdownMenu
// TODO: 4.1.3 - Starter device trait operations selection dropdown
// val operations: List<StarterViewModel.Operation> = StarterViewModel.starterOperations.get(starterTrait.value ?: OnOff)?.operations!!
// for (operation in operations) {
// DropdownMenuItem(
// text = { Text(operation.toString()) },
// onClick = {
// scope.launch {
// starterOperation.value = operation
// }
// expandedOperationSelection = false
// }
// )
// }
}
Batalkan komentar Langkah 4.1.4 dalam file StarterView.kt
untuk merender semua nilai sifat yang dipilih dan menerapkan callback klik dalam DropdownMenu
:
when (starterTrait.value) {
OnOff -> {
...
DropdownMenu(expanded = expandedBooleanSelection, onDismissRequest = { expandedBooleanSelection = false }) {
// TODO: 4.1.4 - Starter device trait values selection dropdown
// for (value in StarterViewModel.valuesOnOff.keys) {
// DropdownMenuItem(
// text = { Text(value.toString()) },
// onClick = {
// scope.launch {
// starterValueOnOff.value = StarterViewModel.valuesOnOff.get(value)
// }
// expandedBooleanSelection = false
// }
// )
// }
}
...
}
LevelControl -> {
...
}
}
Batalkan komentar Langkah 4.1.5 dalam file StarterView.kt
untuk menyimpan semua variabel ViewModel
pemicu ke ViewModel
pemicu otomatisasi draf (draftVM.starterVMs
).
val draftVM: DraftViewModel = homeAppVM.selectedDraftVM.collectAsState().value!!
// Save starter button:
Button(
enabled = isOptionsSelected && isValueProvided,
onClick = {
scope.launch {
// TODO: 4.1.5 - store all starter ViewModel variables into draft ViewModel
// starterVM.deviceVM.emit(starterDeviceVM.value)
// starterVM.trait.emit(starterTrait.value)
// starterVM.operation.emit(starterOperation.value)
// starterVM.valueOnOff.emit(starterValueOnOff.value!!)
// starterVM.valueLevel.emit(starterValueLevel.value!!)
// starterVM.valueBooleanState.emit(starterValueBooleanState.value!!)
// starterVM.valueOccupancy.emit(starterValueOccupancy.value!!)
// starterVM.valueThermostat.emit(starterValueThermostat.value!!)
//
// draftVM.starterVMs.value.add(starterVM)
// draftVM.selectedStarterVM.emit(null)
}
})
{ Text(stringResource(R.string.starter_button_create)) }
Menjalankan aplikasi dan memilih otomatisasi serta pemicu baru akan menampilkan tampilan seperti berikut:
Aplikasi Contoh hanya mendukung starter berdasarkan karakteristik perangkat.
Menyiapkan tindakan
Tindakan otomatisasi mencerminkan tujuan utama otomatisasi, yaitu bagaimana tindakan tersebut memengaruhi perubahan di dunia fisik. Di Aplikasi Contoh, kita mengambil tindakan otomatisasi menggunakan class ActionViewModel
, dan menampilkan tampilan editor menggunakan class ActionView
.
Aplikasi Contoh menggunakan entitas Home API berikut untuk menentukan node tindakan otomatisasi:
- Perangkat
- Ciri
- Perintah
- Nilai (Opsional)
Setiap tindakan perintah perangkat menggunakan perintah, tetapi beberapa tindakan juga akan memerlukan nilai parameter yang terkait dengannya, seperti MoveToLevel()
dan persentase target.
Perangkat dan karakteristik dapat dipilih dari objek yang ditampilkan oleh Devices API.
Aplikasi menentukan daftar perintah yang telah ditentukan sebelumnya:
// List of operations available when creating automation starters:
enum class Action {
ON,
OFF,
MOVE_TO_LEVEL,
MODE_HEAT,
MODE_COOL,
MODE_OFF,
}
Aplikasi melacak operasi yang didukung untuk setiap karakteristik yang didukung:
// List of operations available when comparing booleans:
object OnOffActions : Actions(listOf(
Action.ON,
Action.OFF,
))
// List of operations available when comparing booleans:
object LevelActions : Actions(listOf(
Action.MOVE_TO_LEVEL
))
// List of operations available when comparing booleans:
object ThermostatActions : Actions(listOf(
Action.MODE_HEAT,
Action.MODE_COOL,
Action.MODE_OFF,
))
// Map traits and the comparison operations they support:
val actionActions: Map<TraitFactory<out Trait>, Actions> = mapOf(
OnOff to OnOffActions,
LevelControl to LevelActions,
// BooleanState - No Actions
// OccupancySensing - No Actions
Thermostat to ThermostatActions,
)
Untuk perintah yang menggunakan satu atau beberapa parameter, ada juga variabel:
val valueLevel: MutableStateFlow<UByte?>
API menampilkan serangkaian elemen tampilan yang dapat digunakan pengguna untuk memilih kolom yang diperlukan.
Hapus komentar Langkah 4.2.1 dalam file ActionView.kt
untuk merender semua perangkat tindakan dan menerapkan callback klik dalam DropdownMenu
untuk menyetel actionDeviceVM
.
val deviceVMs = structureVM.deviceVMs.collectAsState().value
...
DropdownMenu(expanded = expandedDeviceSelection, onDismissRequest = { expandedDeviceSelection = false }) {
// TODO: 4.2.1 - Action device selection dropdown
// for (deviceVM in deviceVMs) {
// DropdownMenuItem(
// text = { Text(deviceVM.name) },
// onClick = {
// scope.launch {
// actionDeviceVM.value = deviceVM
// actionTrait.value = null
// actionAction.value = null
// }
// expandedDeviceSelection = false
// }
// )
// }
}
Batalkan komentar Langkah 4.2.2 dalam file ActionView.kt
untuk merender semua sifat actionDeviceVM
dan menerapkan callback klik di DropdownMenu
untuk menetapkan actionTrait
, yang merepresentasikan sifat yang menjadi tujuan perintah.
val actionDeviceVM: MutableState<DeviceViewModel?> = remember {
mutableStateOf(actionVM.deviceVM.value) }
...
DropdownMenu(expanded = expandedTraitSelection, onDismissRequest = { expandedTraitSelection = false }) {
// TODO: 4.2.2 - Action device traits selection dropdown
// val deviceTraits: List<Trait> = actionDeviceVM.value?.traits?.collectAsState()?.value!!
// for (trait in deviceTraits) {
// DropdownMenuItem(
// text = { Text(trait.factory.toString()) },
// onClick = {
// scope.launch {
// actionTrait.value = trait
// actionAction.value = null
// }
// expandedTraitSelection = false
// }
// )
// }
}
Batalkan komentar Langkah 4.2.3 dalam file ActionView.kt
untuk merender semua tindakan actionTrait
yang tersedia dan menerapkan callback klik dalam DropdownMenu
untuk menyetel actionAction
, yang merepresentasikan tindakan otomatisasi yang dipilih.
DropdownMenu(expanded = expandedActionSelection, onDismissRequest = { expandedActionSelection = false }) {
// ...
if (!ActionViewModel.actionActions.containsKey(actionTrait.value?.factory))
return@DropdownMenu
// TODO: 4.2.3 - Action device trait actions (commands) selection dropdown
// val actions: List<ActionViewModel.Action> = ActionViewModel.actionActions.get(actionTrait.value?.factory)?.actions!!
// for (action in actions) {
// DropdownMenuItem(
// text = { Text(action.toString()) },
// onClick = {
// scope.launch {
// actionAction.value = action
// }
// expandedActionSelection = false
// }
// )
// }
}
Hapus tanda komentar pada Langkah 4.2.4 di file ActionView.kt
untuk merender nilai tindakan (perintah) karakteristik yang tersedia dan menyimpan nilai ke actionValueLevel
dalam callback perubahan nilai:
when (actionTrait.value?.factory) {
LevelControl -> {
// TODO: 4.2.4 - Action device trait action(command) values selection widget
// Column (Modifier.padding(horizontal = 16.dp, vertical = 8.dp).fillMaxWidth()) {
// Text(stringResource(R.string.action_title_value), fontSize = 16.sp, fontWeight = FontWeight.SemiBold)
// }
//
// Box (Modifier.padding(horizontal = 24.dp, vertical = 8.dp)) {
// LevelSlider(value = actionValueLevel.value?.toFloat()!!, low = 0f, high = 254f, steps = 0,
// modifier = Modifier.padding(top = 16.dp),
// onValueChange = { value : Float -> actionValueLevel.value = value.toUInt().toUByte() }
// isEnabled = true
// )
// }
...
}
Batalkan komentar Langkah 4.2.5 dalam file ActionView.kt
untuk menyimpan semua variabel ViewModel
tindakan dalam ViewModel
tindakan otomatisasi draf (draftVM.actionVMs
):
val draftVM: DraftViewModel = homeAppVM.selectedDraftVM.collectAsState().value!!
// Save action button:
Button(
enabled = isOptionsSelected,
onClick = {
scope.launch {
// TODO: 4.2.5 - store all action ViewModel variables into draft ViewModel
// actionVM.deviceVM.emit(actionDeviceVM.value)
// actionVM.trait.emit(actionTrait.value)
// actionVM.action.emit(actionAction.value)
// actionVM.valueLevel.emit(actionValueLevel.value)
//
// draftVM.actionVMs.value.add(actionVM)
// draftVM.selectedActionVM.emit(null)
}
})
{ Text(stringResource(R.string.action_button_create)) }
Menjalankan aplikasi dan memilih otomatisasi serta tindakan baru akan menghasilkan tampilan seperti berikut:
Kami hanya mendukung tindakan berdasarkan karakteristik perangkat di Aplikasi Contoh.
Merender otomatisasi draf
Setelah DraftViewModel
selesai, DraftViewModel
dapat dirender oleh HomeAppView.kt
:
fun HomeAppView (homeAppVM: HomeAppViewModel) {
...
// If a draft automation is selected, show the draft editor:
if (selectedDraftVM != null) {
DraftView(homeAppVM)
}
...
}
Di DraftView.kt
:
fun DraftView (homeAppVM: HomeAppViewModel) {
val draftVM: DraftViewModel = homeAppVM.selectedDraftVM.collectAsState().value!!
...
// Draft Starters:
DraftStarterList(draftVM)
// Draft Actions:
DraftActionList(draftVM)
}
Membuat otomatisasi
Setelah mempelajari cara membuat pemicu dan tindakan, Anda siap membuat draf otomatisasi dan mengirimkannya ke Automation API. API memiliki fungsi createAutomation()
yang menggunakan draf otomatisasi sebagai argumen, dan menampilkan instance otomatisasi baru.
Persiapan otomatisasi draf dilakukan di class DraftViewModel
di Aplikasi Contoh. Lihat fungsi getDraftAutomation()
untuk mempelajari lebih lanjut cara kami menyusun draf otomatisasi menggunakan variabel starter dan tindakan di bagian sebelumnya.
Hapus komentar Langkah 4.4.1 dalam file DraftViewModel.kt
untuk membuat ekspresi "select" yang diperlukan untuk membuat grafik otomatisasi saat sifat awal adalah OnOff
:
val starterVMs: List<StarterViewModel> = starterVMs.value
val actionVMs: List<ActionViewModel> = actionVMs.value
...
fun getDraftAutomation() : DraftAutomation {
...
val starterVMs: List<StarterViewModel> = starterVMs.value
...
return automation {
this.name = name
this.description = description
this.isActive = true
// The sequential block wrapping all nodes:
sequential {
// The select block wrapping all starters:
select {
// Iterate through the selected starters:
for (starterVM in starterVMs) {
// The sequential block for each starter (should wrap the Starter Expression!)
sequential {
...
val starterTrait: TraitFactory<out Trait> = starterVM.trait.value!!
...
when (starterTrait) {
OnOff -> {
// TODO: 4.4.1 - Set starter expressions according to trait type
// val onOffValue: Boolean = starterVM.valueOnOff.value
// val onOffExpression: TypedExpression<out OnOff> =
// starterExpression as TypedExpression<out OnOff>
// when (starterOperation) {
// StarterViewModel.Operation.EQUALS ->
// condition { expression = onOffExpression.onOff equals onOffValue }
// StarterViewModel.Operation.NOT_EQUALS ->
// condition { expression = onOffExpression.onOff notEquals onOffValue }
// else -> { MainActivity.showError(this, "Unexpected operation for OnOf
// }
}
LevelControl -> {
...
// Function to allow manual execution of the automation:
manualStarter()
...
}
Batalkan komentar Langkah 4.4.2 dalam file DraftViewModel.kt
untuk membuat ekspresi paralel yang diperlukan untuk membuat grafik otomatisasi saat sifat tindakan yang dipilih adalah LevelControl
dan tindakan yang dipilih adalah MOVE_TO_LEVEL
:
val starterVMs: List<StarterViewModel> = starterVMs.value
val actionVMs: List<ActionViewModel> = actionVMs.value
...
fun getDraftAutomation() : DraftAutomation {
...
return automation {
this.name = name
this.description = description
this.isActive = true
// The sequential block wrapping all nodes:
sequential {
...
// Parallel block wrapping all actions:
parallel {
// Iterate through the selected actions:
for (actionVM in actionVMs) {
val actionDeviceVM: DeviceViewModel = actionVM.deviceVM.value!!
// Action Expression that the DSL will check for:
action(actionDeviceVM.device, actionDeviceVM.type.value.factory) {
val actionCommand: Command = when (actionVM.action.value) {
ActionViewModel.Action.ON -> { OnOff.on() }
ActionViewModel.Action.OFF -> { OnOff.off() }
// TODO: 4.4.2 - Set starter expressions according to trait type
// ActionViewModel.Action.MOVE_TO_LEVEL -> {
// LevelControl.moveToLevelWithOnOff(
// actionVM.valueLevel.value!!,
// 0u,
// LevelControlTrait.OptionsBitmap(),
// LevelControlTrait.OptionsBitmap()
// )
// }
ActionViewModel.Action.MODE_HEAT -> { SimplifiedThermostat
.setSystemMode(SimplifiedThermostatTrait.SystemModeEnum.Heat) }
...
}
Langkah terakhir untuk menyelesaikan otomatisasi adalah menerapkan fungsi getDraftAutomation
untuk membuat AutomationDraft.
Batalkan komentar Langkah 4.4.3 di file HomeAppViewModel.kt
untuk membuat otomatisasi dengan memanggil Home API dan menangani pengecualian:
fun createAutomation(isPending: MutableState<Boolean>) {
viewModelScope.launch {
val structure : Structure = selectedStructureVM.value?.structure!!
val draft : DraftAutomation = selectedDraftVM.value?.getDraftAutomation()!!
isPending.value = true
// TODO: 4.4.3 - Call the Home API to create automation and handle exceptions
// // Call Automation API to create an automation from a draft:
// try {
// structure.createAutomation(draft)
// }
// catch (e: Exception) {
// MainActivity.showError(this, e.toString())
// isPending.value = false
// return@launch
// }
// Scrap the draft and automation candidates used in the process:
selectedCandidateVMs.emit(null)
selectedDraftVM.emit(null)
isPending.value = false
}
}
Sekarang jalankan aplikasi dan lihat perubahannya di perangkat Anda.
Setelah memilih pemicu dan tindakan, Anda siap membuat otomatisasi:
Pastikan Anda memberi nama otomatisasi dengan sesuatu yang unik, lalu ketuk tombol Create Automation, yang akan memanggil API dan mengarahkan Anda kembali ke tampilan daftar otomatisasi dengan otomatisasi Anda:
Ketuk otomatisasi yang baru saja Anda buat, dan lihat cara otomatisasi tersebut ditampilkan oleh API.
Perhatikan bahwa API menampilkan nilai yang menunjukkan apakah otomatisasi valid dan aktif saat ini atau tidak. Anda dapat membuat otomatisasi yang tidak lulus validasi saat diuraikan di sisi server. Jika penguraian otomatisasi gagal divalidasi, isValid
akan disetel ke false
, yang menunjukkan bahwa otomatisasi tidak valid dan tidak aktif. Jika otomatisasi Anda tidak valid, periksa kolom automation.validationIssues
untuk mengetahui detailnya.
Pastikan otomatisasi Anda ditetapkan sebagai valid dan aktif, lalu Anda dapat mencoba otomatisasi tersebut.
Mencoba otomatisasi
Otomatisasi dapat dijalankan dengan dua cara:
- Dengan peristiwa pemicu. Jika kondisi cocok, tindakan yang Anda tetapkan dalam otomatisasi akan dipicu.
- Dengan panggilan API eksekusi manual.
Jika otomatisasi draf memiliki manualStarter()
yang ditentukan dalam blok DSL draf otomatisasi, mesin otomatisasi akan mendukung eksekusi manual untuk otomatisasi tersebut. Hal ini sudah ada dalam contoh kode di Aplikasi Contoh.
Karena Anda masih berada di layar tampilan otomatisasi di perangkat seluler, ketuk tombol Eksekusi Manual. Tindakan ini akan memanggil automation.execute()
, yang menjalankan perintah tindakan Anda di perangkat yang Anda pilih saat menyiapkan otomatisasi.
Setelah Anda memvalidasi perintah tindakan melalui eksekusi manual menggunakan API, sekarang saatnya untuk melihat apakah perintah tersebut juga dieksekusi menggunakan pemicu yang Anda tentukan.
Buka Tab Perangkat, pilih perangkat tindakan dan sifat, lalu tetapkan ke nilai yang berbeda (misalnya, tetapkan light2
's LevelControl
(kecerahan) ke 50%, seperti yang diilustrasikan dalam screenshot berikut:
Sekarang kita akan mencoba memicu otomatisasi menggunakan perangkat pemicu. Pilih perangkat pemicu yang Anda pilih saat membuat otomatisasi. Alihkan sifat yang Anda pilih (misalnya, tetapkan OnOff
starter outlet1
ke On
):
Anda akan melihat bahwa hal ini juga menjalankan otomatisasi dan menyetel karakteristik LevelControl
perangkat tindakan light2
ke nilai aslinya, 100%:
Selamat, Anda berhasil menggunakan Home API untuk membuat otomatisasi.
Untuk mempelajari lebih lanjut Automation API, lihat Android Automation API.
5. Menemukan Kemampuan
Home API mencakup API khusus yang disebut Discovery API, yang dapat digunakan developer untuk membuat kueri tentang sifat yang kompatibel dengan otomatisasi yang didukung di perangkat tertentu. Aplikasi Contoh memberikan contoh tempat Anda dapat menggunakan API ini untuk menemukan perintah yang tersedia.
Menemukan Perintah
Di bagian ini, kita membahas cara menemukan CommandCandidates
yang didukung dan cara membuat otomatisasi berdasarkan node kandidat yang ditemukan.
Di Aplikasi Contoh, kita memanggil device.candidates()
untuk mendapatkan daftar kandidat, yang dapat mencakup instance CommandCandidate
, EventCandidate
, atau TraitAttributesCandidate
.
Buka file HomeAppViewModel.kt
dan hapus komentar Langkah 5.1.1 untuk mengambil daftar kandidat dan memfilter dengan jenis Candidate
:
fun showCandidates() {
...
// TODO: 5.1.1 - Retrieve automation candidates, filtering to include CommandCandidate types only
// // Retrieve a set of initial automation candidates from the device:
// val candidates: Set<NodeCandidate> = deviceVM.device.candidates().first()
//
// for (candidate in candidates) {
// // Check whether the candidate trait is supported:
// if(candidate.trait !in HomeApp.supportedTraits)
// continue
// // Check whether the candidate type is supported:
// when (candidate) {
// // Command candidate type:
// is CommandCandidate -> {
// // Check whether the command candidate has a supported command:
// if (candidate.commandDescriptor !in ActionViewModel.commandMap)
// continue
// }
// // Other candidate types are currently unsupported:
// else -> { continue }
// }
//
// candidateVMList.add(CandidateViewModel(candidate, deviceVM))
// }
...
// Store the ViewModels:
selectedCandidateVMs.emit(candidateVMList)
}
Lihat cara memfilter CommandCandidate.
Kandidat yang ditampilkan oleh API termasuk dalam berbagai jenis. Aplikasi Contoh mendukung CommandCandidate
. Hapus komentar Langkah 5.1.2 di commandMap
yang ditentukan dalam ActionViewModel.kt
untuk menetapkan sifat yang didukung ini:
// Map of supported commands from Discovery API:
val commandMap: Map<CommandDescriptor, Action> = mapOf(
// TODO: 5.1.2 - Set current supported commands
// OnOffTrait.OnCommand to Action.ON,
// OnOffTrait.OffCommand to Action.OFF,
// LevelControlTrait.MoveToLevelWithOnOffCommand to Action.MOVE_TO_LEVEL
)
Setelah dapat memanggil Discovery API, dan memfilter hasil yang kami dukung di Aplikasi Contoh, kita akan membahas cara mengintegrasikannya ke editor.
Untuk mempelajari Discovery API lebih lanjut, buka Memanfaatkan penemuan perangkat di Android.
Mengintegrasikan editor
Cara paling umum untuk menggunakan tindakan yang ditemukan adalah dengan menampilkannya kepada pengguna akhir untuk dipilih. Tepat sebelum pengguna memilih kolom otomatisasi draf, kita dapat menampilkan daftar tindakan yang ditemukan, dan bergantung pada nilai yang dipilih, kita dapat mengisi otomatis node tindakan dalam draf otomatisasi.
File CandidatesView.kt
berisi class tampilan yang menampilkan kandidat yang ditemukan. Hapus komentar Langkah 5.2.1 untuk mengaktifkan fungsi .clickable{}
CandidateListItem
yang menetapkan homeAppVM.selectedDraftVM
sebagai candidateVM
:
fun CandidateListItem (candidateVM: CandidateViewModel, homeAppVM: HomeAppViewModel) {
val scope: CoroutineScope = rememberCoroutineScope()
Box (Modifier.padding(horizontal = 24.dp, vertical = 8.dp)) {
Column (Modifier.fillMaxWidth().clickable {
// TODO: 5.2.1 - Set the selectedDraftVM to the selected candidate
// scope.launch { homeAppVM.selectedDraftVM.emit(DraftViewModel(candidateVM)) }
}) {
...
}
}
}
Mirip dengan Langkah 4.3 di HomeAppView.kt
, saat selectedDraftVM
disetel, DraftView(...) in
DraftView.kt akan dirender:
fun HomeAppView (homeAppVM: HomeAppViewModel) {
...
val selectedDraftVM: DraftViewModel? by homeAppVM.selectedDraftVM.collectAsState()
...
// If a draft automation is selected, show the draft editor:
if (selectedDraftVM != null) {
DraftView(homeAppVM)
}
...
}
Coba lagi dengan mengetuk light2 - MOVE_TO_LEVEL, yang ditampilkan di bagian sebelumnya, yang akan meminta Anda membuat otomatisasi baru berdasarkan perintah kandidat:
Setelah memahami cara membuat otomatisasi di Aplikasi Contoh, Anda dapat mengintegrasikan otomatisasi di aplikasi Anda.
6. Contoh Otomatisasi Lanjutan
Sebelum mengakhiri, kita akan membahas beberapa contoh DSL otomatisasi tambahan. Contoh ini menggambarkan beberapa kemampuan lanjutan yang dapat Anda capai dengan API.
Waktu dalam Sehari sebagai Pemicu
Selain sifat perangkat, Google Home API menawarkan sifat berbasis struktur, seperti Time
. Anda dapat membuat otomatisasi yang memiliki pemicu berbasis waktu, seperti berikut:
automation {
name = "AutomationName"
description = "An example automation description."
isActive = true
description = "Do ... actions when time is up."
sequential {
// starter
val starter = starter<_>(structure, Time.ScheduledTimeEvent) {
parameter(
Time.ScheduledTimeEvent.clockTime(
LocalTime.of(hour, min, sec, 0)
)
)
}
// action
...
}
}
Siaran Asisten sebagai Tindakan
Ciri AssistantBroadcast
tersedia sebagai ciri tingkat perangkat di SpeakerDevice
(jika speaker mendukungnya) atau sebagai ciri tingkat struktur (karena speaker Google dan perangkat seluler Android dapat memutar siaran Asisten). Contoh:
automation {
name = "AutomationName"
description = "An example automation description."
isActive = true
description = "Broadcast in Speaker when ..."
sequential {
// starter
...
// action
action(structure) {
command(
AssistantBroadcast.broadcast("Time is up!!")
)
}
}
}
Menggunakan DelayFor
dan suppressFor
Automation API juga menyediakan operator lanjutan seperti delayFor, yang digunakan untuk menunda perintah, dan suppressFor, yang dapat mencegah pemicuan otomatisasi oleh peristiwa yang sama dalam rentang waktu tertentu. Berikut beberapa contoh penggunaan operator ini:
sequential {
val starterNode = starter<_>(device, OccupancySensorDevice, MotionDetection)
// only proceed if there is currently motion taking place
condition { starterNode.motionDetectionEventInProgress equals true }
// ignore the starter for one minute after it was last triggered
suppressFor(Duration.ofMinutes(1))
// make announcements three seconds apart
action(device, SpeakerDevice) {
command(AssistantBroadcast.broadcast("Intruder detected!"))
}
delayFor(Duration.ofSeconds(3))
action(device, SpeakerDevice) {
command(AssistantBroadcast.broadcast("Intruder detected!"))
}
...
}
Menggunakan AreaPresenceState
di starter
AreaPresenceState
adalah sifat tingkat struktur yang mendeteksi apakah ada orang di rumah.
Misalnya, contoh berikut menunjukkan cara mengunci pintu secara otomatis saat seseorang berada di rumah setelah pukul 22.00:
automation {
name = "Lock the doors when someone is home after 10pm"
description = "1 starter, 2 actions"
sequential {
val unused =
starter(structure, event = Time.ScheduledTimeEvent) {
parameter(Time.ScheduledTimeEvent.clockTime(LocalTime.of(22, 0, 0, 0)))
}
val stateReaderNode = stateReader<_>(structure, AreaPresenceState)
condition {
expression =
stateReaderNode.presenceState equals
AreaPresenceStateTrait.PresenceState.PresenceStateOccupied
}
action(structure) { command(AssistantBroadcast.broadcast("Locks are being applied")) }
for (lockDevice in lockDevices) {
action(lockDevice, DoorLockDevice) {
command(Command(DoorLock, DoorLockTrait.LockDoorCommand.requestId.toString(), mapOf()))
}
}
}
Setelah Anda memahami kemampuan otomatisasi lanjutan ini, mulailah membuat aplikasi yang luar biasa.
7. Selamat!
Selamat! Anda berhasil menyelesaikan bagian kedua pengembangan Aplikasi Android menggunakan Google Home API. Selama codelab ini, Anda telah menjelajahi API Otomatisasi dan Penemuan.
Kami harap Anda menikmati pembuatan aplikasi yang secara kreatif mengontrol perangkat dalam ekosistem Google Home dan membangun skenario otomatisasi yang menarik menggunakan API Home.
Langkah berikutnya
- Baca Pemecahan Masalah untuk mempelajari cara men-debug aplikasi secara efektif dan memecahkan masalah yang melibatkan Home API.
- Anda dapat menghubungi kami untuk memberikan rekomendasi atau melaporkan masalah melalui Issue Tracker, topik dukungan Smart Home.