Discovery API dimaksudkan untuk digunakan oleh aplikasi yang dapat membuat otomatisasi berdasarkan perangkat yang ada di rumah pengguna. Hal ini dapat mengungkapkan kepada aplikasi saat runtime karakteristik dan perangkat yang ada dalam struktur tertentu untuk digunakan dalam otomatisasi. Selain itu, API ini mengekspos perintah, atribut, dan peristiwa terkait, serta rentang nilai yang diizinkan untuk parameter dan kolom.
Discovery API mengabaikan perangkat atau karakteristik apa pun yang ada dalam struktur yang
tidak didukung oleh Automation API, serta perangkat atau karakteristik apa pun yang
tidak terdaftar di
FactoryRegistry
. Lihat
Membuat instance Home untuk mengetahui informasi selengkapnya tentang penggunaan
FactoryRegistry
.
Menggunakan API
Inti Discovery API adalah antarmuka
HasCandidates
, yang merupakan root hierarki jenis yang mencakup Structure
,
Room
, dan HomeDevice
.
Antarmuka
HasCandidates
menentukan dua metode, candidates()
dan allCandidates()
yang
menampilkan objek Flow
.
candidates()
menghasilkan daftar kandidat otomatisasi untuk entitas (Structure
,Room
,HomeDevice
).allCandidates()
menghasilkan daftar kandidat otomatisasi untuk entity dan semua turunannya. Metode ini tidak didukung olehRoom
.
Tidak seperti objek Flow
lainnya yang ditampilkan oleh Home API, objek ini berisi
snapshot satu kali.
Untuk mendapatkan daftar kandidat terbaru yang tersedia, developer
harus memanggil candidates()
atau allCandidates()
setiap kali, dan tidak dapat hanya memanggil
collect()
pada objek Flow
. Selain itu, karena kedua metode ini
sangat membutuhkan resource, memanggilnya lebih dari sekali per menit akan
menghasilkan data yang di-cache, yang mungkin tidak mencerminkan status
aktual saat itu.
Antarmuka
NodeCandidate
mewakili node kandidat yang ditemukan oleh kedua metode ini, dan merupakan
root hierarki yang menyertakan antarmuka berikut:
dan class berikut:
Menggunakan kandidat otomatisasi
Misalnya, Anda menulis aplikasi yang membuat otomatisasi untuk menutup serangkaian
tirai smart pada waktu yang ditentukan pengguna. Namun, Anda tidak tahu apakah pengguna memiliki
perangkat yang mendukung sifat WindowCovering
dan apakah WindowCovering
atau atribut atau perintahnya dapat digunakan dalam otomatisasi.
Kode berikut mengilustrasikan cara menggunakan Discovery API untuk memfilter
output metode candidates()
guna mempersempit hasil dan mendapatkan
jenis elemen tertentu (struktur, peristiwa, perintah) yang dicari. Pada akhirnya,
alat ini akan membuat otomatisasi dari elemen yang dikumpulkan.
import com.google.home.Structure
import com.google.home.automation.CommandCandidate
import com.google.home.automation.EventCandidate
import com.google.home.automation.Automation
import com.google.home.automation.DraftAutomation
import com.google.home.platform.Time
import java.time.LocalTime
import com.google.home.matter.standard.WindowCoveringTrait
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
fun createAutomationWithDiscoveryApiTimeStarter(
structureName: String,
scheduledTimeInSecond: Int,
): String = runBlocking {
// get the Structure
val structure = homeClient.structures().findStructureByName(structureName)
// get an event candidate
val clockTimeStarter =
structure
.allCandidates().first().firstOrNull {candidate ->
candidate is EventCandidate && candidate.eventFactory == Time.ScheduledTimeEvent
} as EventCandidate
// retrieve the first 'DownOrClose' command encountered
val downOrCloseCommand =
structure.allCandidates().first().firstOrNull {
candidate ->
candidate is CommandCandidate
&& candidate.commandDescriptor == WindowCoveringTrait.DownOrCloseCommand
} as CommandCandidate
val blinds = ...
// prompt user to select the WindowCoveringDevice
...
if (clockTimeStarter && downOrCloseCommand && blinds) {
// Create the draft automation
val draftAutomation: DraftAutomation = automation {
name = ""
description = ""
isActive = true
sequential {
val mainStarter = starter<_>(structure, Time.ScheduledTimeEvent) {
parameter(
Time.ScheduledTimeEvent.clockTime(
LocalTime.ofSecondOfDay(scheduledTimeInSecond.toLong())
)
)
}
action(blinds, WindowCoveringDevice) { command(WindowCoveringTrait.downOrClose())
}
}
// Create the automation in the structure
val automation = structure.createAutomation(draftAutomation)
return@runBlocking automation.id.id
} else ... //the automation cannot be created
Contoh berikut membuat otomatisasi untuk menetapkan tingkat kecerahan lampu saat dinyalakan.
import com.google.home.Structure
import com.google.home.automation.CommandCandidate
import com.google.home.automation.TraitAttributesCandidate
import com.google.home.automation.Automation
import com.google.home.automation.DraftAutomation
import com.google.home.matter.standard.LevelControl
import com.google.home.matter.standard.LevelControlTrait.MoveToLevelCommand
import com.google.home.matter.standard.OnOff
import com.google.home.matter.standard.OnOff.Companion.onOff
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import com.google.home.automation.equals
fun createAutomationWithDiscoveryApiDimLight(
structureName: String,
): String = runBlocking {
// get the Structure
val structure: Structure = homeClient.structures().findStructureByName(structureName)
/**
* When I turn on the light, move the brightness level to 55
*/
val allCandidates = structure.allCandidates().first()
val dimmableLightDevice = structure.devices().list().first {it.has(OnOff) && it.has(LevelControl)}
val starterCandidate =
allCandidates
.filterIsInstance<TraitAttributesCandidate>()
.first { it.entity == dimmableLightDevice && it.trait == OnOff }
val actionCandidate =
allCandidates
.filterIsInstance<CommandCandidate>()
.first {it.entity == dimmableLightDevice && it.commandDescriptor == MoveToLevelCommand }
if (starterCandidate && actionCandidate) {
// Create the draft automation
val draftAutomation: DraftAutomation = automation {
sequential {
val starter = starter<_>(dimmableLightDevice, OnOffLightDevice, OnOff)
condition { expression = starter.onOff equals true }
action(dimmableLightDevice,DimmableLightDevice) {
mapOf(MoveToLevelCommand.Request.CommandFields.level to 55u.toUByte())
)
}
}
// Create the automation in the structure
val automation = structure.createAutomation(draftAutomation)
return@runBlocking automation.id.id
}
} else ... //the automation cannot be created
Memeriksa prasyarat
Discovery API memungkinkan Anda mengetahui bahwa suatu karakteristik tidak memiliki prasyarat untuk digunakan, seperti
langganan atau alamat struktur.
Hal ini dilakukan menggunakan atribut
unsupportedReasons
class Candidate
. Atribut ini diisi dengan
UnsupportedCandidateReason
selama panggilan candidates()
. Dan informasi yang sama muncul dalam
pesan error validasi saat createAutomation()
dipanggil.
Contoh alasan:
MissingStructureAddressSetup
memberi tahu pengguna bahwa penyiapan alamat diperlukan untuk menggunakan karakteristikTime
. Mengubah alamat rumah Google menjelaskan cara pengguna memasukkan alamat struktur menggunakan Google Home app (GHA).MissingPresenceSensingSetup
memberi tahu pengguna bahwa penyiapan kehadiran diperlukan untuk menggunakan sifatAreaPresenceState
danAreaAttendanceState
.MissingSubscription
memberi tahu pengguna bahwa langganan Nest Aware diperlukan untuk menggunakan karakteristikObjectDetection
.
Misalnya, untuk menangani MissingStructureAddressSetup
UnsupportedCandidateReason
, Anda mungkin ingin menampilkan
toast
di aplikasi dan membuka GHA untuk memungkinkan pengguna memberikan
alamat struktur:
val structure = homeManager.structures().list().single()
val allCandidates = structure.allCandidates().list().single()
val scheduledStarterCandidate = allCandidates.first { it is EventCandidate && it.eventFactory == ScheduledTimeEvent }
if (scheduledStarterCandidate.unsupportedReasons.any { it is MissingStructureAddressSetup }) {
showToast("No Structure Address setup. Redirecting to GHA to set up an address.")
launchChangeAddress(...)
}
Memvalidasi parameter
Discovery API menampilkan nilai yang diizinkan untuk atribut, parameter, atau kolom peristiwa, dalam bentuk instance Constraint
. Informasi ini memungkinkan developer aplikasi mencegah pengguna
menetapkan nilai yang tidak valid.
Setiap subclass Constraint
memiliki cara tersendiri untuk merepresentasikan
nilai yang diterima.
Class batasan | Properti yang mewakili nilai yang diterima |
---|---|
BitmapConstraint |
combinedBits
|
BooleanConstraint | |
ByteConstraint |
maxLength dan minLength
|
EnumConstraint |
allowedSet
|
NumberRangeConstraint |
lowerBound , upperBound , step ,
dan unit
|
NumberSetConstraint |
allowedSet dan unit
|
StringConstraint |
allowedSet , disallowedSet , isCaseSensitive ,
maxLength , minLength , dan
regex
|
StructConstraint |
fieldConstraints
|
ListConstraint |
elementConstraint
|
Menggunakan batasan
Misalnya, Anda menulis aplikasi yang membuat otomatisasi yang menetapkan tingkat
perangkat dengan
sifat
LevelControl
. Contoh berikut menunjukkan cara memastikan bahwa nilai yang digunakan untuk
menetapkan atribut
currentLevel
sifat LevelControl
berada dalam batas yang diterima.
import android.content.Context
import com.google.home.Home
import com.google.home.Structure
import com.google.home.automation.Action
import com.google.home.automation.Automation
import com.google.home.automation.CommandCandidate
import com.google.home.automation.Condition
import com.google.home.automation.Constraint
import com.google.home.automation.Equals
import com.google.home.automation.EventCandidate
import com.google.home.automation.HasCandidates
import com.google.home.automation.Node
import com.google.home.automation.NodeCandidate
import com.google.home.automation.SequentialFlow
import com.google.home.automation.Starter
import com.google.home.matter.standard.LevelControlTrait
// Filter the output of candidates() to find the TraitAttributesCandidate
// for the LevelControl trait.
val levelCommand =
structure
.allCandidates()
.first()
.firstOrNull { candidate ->
candidate is CommandCandidate && candidate.command == LevelControlTrait.MoveToLevelCommand
} as? CommandCandidate
var levelConstraint = null
// Get the NodeCandidate instance's fieldDetailsMap and
// retrieve the Constraint associated with the level parameter.
// In this case, it is a NumberRangeConstraint.
if (levelCommand != null) {
levelConstraint =
levelCommand.fieldDetailsMap[
LevelControlTrait.MoveToLevelCommand.Request.CommandFields.level
]!!.constraint
}
...
// Test the value against the Constraint (ignoring step and unit)
if ( value in levelConstraint.lowerBound..levelConstraint.upperBound) {
// ok to use the value
}
Membandingkan Device API dan Discovery API
Anda dapat menemukan jenis perangkat, karakteristik, dan atributnya tanpa menggunakan Discovery API. Dengan menggunakan Device API, Anda dapat menemukan:
- Jenis perangkat utama yang telah memberikan izin kepada developer untuk
kontrol, menggunakan
metode
DeviceType.Metadata.isPrimaryType()
. - Apakah setiap perangkat mendukung semua karakteristik yang diperlukan otomatisasi, menggunakan
metode
HasTraits.has()
. - Apakah setiap karakteristik mendukung semua
atribut dan
perintah yang diperlukan
otomatisasi, menggunakan metode
supports()
.
Penting untuk diperhatikan bahwa jika menggunakan Device API untuk melakukan penemuan, Anda tidak akan mendapatkan manfaat dari kemampuan Discovery API berikut:
- Pemfilteran otomatis dari karakteristik yang tidak didukung oleh Automation API.
- Kemampuan untuk memberi pengguna opsi guna memilih nilai yang valid untuk atribut dan parameter yang menggunakan batasan.