Discovery API, kullanıcının evinde bulunan cihazlara göre otomasyon oluşturabilen uygulamalar tarafından kullanılmak üzere tasarlanmıştır. Çalıştırma sırasında bir uygulamaya, otomasyonlarda kullanılmak üzere belirli bir yapıda hangi özelliklerin ve cihazların bulunduğunu gösterebilir. Ayrıca, ilişkili komutları, özellikleri ve etkinlikleri, ayrıca parametreler ve alanlar için izin verilen değer aralığını gösterir.
Discovery API, Automation API tarafından desteklenmeyen bir yapıda bulunan cihazları veya özellikleri ve FactoryRegistry
'e kaydedilmemiş cihazları veya özellikleri yoksayar. FactoryRegistry
kullanma hakkında daha fazla bilgi için Home örneği oluşturma başlıklı makaleyi inceleyin.
API'yi kullanma
Discovery API'nin merkezinde, Structure
, Room
ve HomeDevice
içeren bir tür hiyerarşisinin kökü olan HasCandidates
arayüzü bulunur.
HasCandidates
arayüzü, ikisi de Flow
nesnesi döndüren candidates()
ve allCandidates()
olmak üzere iki yöntem tanımlar.
candidates()
, öğe (Structure
,Room
,HomeDevice
) için otomasyon adaylarının listesini oluşturur.allCandidates()
, öğe ve tüm alt öğeleri için otomasyon adaylarının listesini oluşturur. Bu yöntemRoom
tarafından desteklenmiyor.
Home API'leri tarafından döndürülen diğer Flow
nesnelerinin aksine, bunlar tek seferlik bir anlık görüntü içerir.
Geliştirici, kullanılabilir adayların en güncel listesini almak için her seferinde candidates()
veya allCandidates()
çağrısını yapmalıdır ve Flow
nesnelerinde yalnızca collect()
çağrısını yapamaz. Ayrıca, bu iki yöntem özellikle kaynak yoğun olduğundan, bunları dakikada bir defadan daha sık çağırmak, önbelleğe alınmış verilerin döndürülmesine neden olur. Bu veriler, o andaki gerçek durumu yansıtmayabilir.
NodeCandidate
arayüzü, bu iki yöntemle bulunan bir aday düğümü temsil eder ve aşağıdaki arayüzleri içeren bir hiyerarşinin köküdür:
ve aşağıdaki sınıflar:
Otomasyon adayları ile çalışma
Bir akıllı perde grubunu kullanıcı tarafından belirtilen bir zamanda kapatmak için otomasyon oluşturan bir uygulama yazdığınızı varsayalım. Ancak kullanıcının WindowCovering
özelliğini destekleyen bir cihazı bulup bulmadığını ve WindowCovering
'nin ya da özelliklerinden veya komutlarından herhangi birinin otomasyonlarda kullanılıp kullanılamayacağını bilmiyorsunuz.
Aşağıdaki kodda, Discovery API'nin sonuçları daraltmak ve aranan belirli öğe türünü (yapı, etkinlik, komut) elde etmek için candidates()
yönteminin çıktısını filtrelemek üzere nasıl kullanılacağı gösterilmektedir. Sonunda, toplanan öğelerden bir otomasyon oluşturur.
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
Aşağıdaki örnekte, ışık açıldığında parlaklığını ayarlamak için bir otomasyon oluşturulmaktadır.
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
Önkoşulları kontrol etme
Discovery API, bir özelliğin kullanımı için gerekli ön koşulların (ör. abonelik veya yapı adresi) eksik olduğunu size bildirir.
Bunu Candidate
sınıfının unsupportedReasons
özelliğini kullanarak yapar. Bu özellik, candidates()
çağrısı sırasında bir UnsupportedCandidateReason
ile doldurulur. createAutomation()
çağrıldığında da aynı bilgiler doğrulama hata mesajlarında görünür.
Örnek nedenler:
MissingStructureAddressSetup
, kullanıcıyaTime
özelliğini kullanmak için adres ayarının gerekli olduğunu bildirir. Google ana sayfa adresini değiştirme başlıklı makalede, kullanıcının Google Home app (GHA)'yi kullanarak yapı adresini nasıl girebileceği açıklanmaktadır.MissingPresenceSensingSetup
,AreaPresenceState
veAreaAttendanceState
özelliklerini kullanmak için varlığın ayarlanması gerektiğini kullanıcıya bildirir.MissingSubscription
,ObjectDetection
özelliğini kullanmak için Nest Aware aboneliğinin gerekli olduğunu kullanıcıya bildirir.
Örneğin, MissingStructureAddressSetup
UnsupportedCandidateReason
ile ilgili işlemleri yapmak için uygulamanızda bir toast gösterebilir ve kullanıcının yapının adresini sağlamasına izin vermek için GHA'i açabilirsiniz:
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(...)
}
Parametreleri doğrulama
Discovery API, bir özellik, parametre veya etkinlik alanında izin verilen değerleri Constraint
örneği biçiminde döndürür. Bu bilgiler, uygulama geliştiricinin kullanıcıların geçersiz değerler ayarlamasını engellemesine olanak tanır.
Constraint
alt sınıflarının her birinin kabul edilen değerleri temsil etme şekli farklıdır.
Kısıtlama sınıfı | Kabul edilen değerleri temsil eden özellikler |
---|---|
BitmapConstraint |
combinedBits
|
BooleanConstraint | |
ByteConstraint |
maxLength ve minLength
|
EnumConstraint |
allowedSet
|
NumberRangeConstraint |
lowerBound , upperBound , step
ve unit
|
NumberSetConstraint |
allowedSet ve unit
|
StringConstraint |
allowedSet , disallowedSet , isCaseSensitive ,
maxLength , minLength ve
regex
|
StructConstraint |
fieldConstraints
|
ListConstraint |
elementConstraint
|
Kısıtlamaları kullanma
LevelControl
özelliğine sahip bir cihazın seviyesini ayarlayan bir otomasyon oluşturan bir uygulama yazdığınızı varsayalım. Aşağıdaki örnekte, LevelControl
özelliğinin currentLevel
özelliğini ayarlamak için kullanılan değerin kabul edilen sınırlar içinde olduğundan nasıl emin olacağınız gösterilmektedir.
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
}
Device API ve Discovery API'yi karşılaştırma
Discovery API'yi kullanmadan cihaz türlerini, özelliklerini ve özelliklerini keşfedebilirsiniz. Device API'yi kullanarak şunları keşfedebilirsiniz:
- Kullanıcının,
DeviceType.Metadata.isPrimaryType()
yöntemini kullanarak geliştiriciye kontrol için izin verdiği birincil cihaz türleri. HasTraits.has()
yöntemini kullanarak her cihazın otomasyonun gerektirdiği tüm özellikleri destekleyip desteklemediğini belirtir.- Her özelliğin, otomasyonun gerektirdiği tüm özellikleri ve komutları destekleyip desteklemediğini
supports()
yöntemini kullanarak kontrol edin.
Keşif yapmak için Device API'yi kullanırsanız aşağıdaki Discovery API özelliklerinden yararlanamayacağınızı unutmayın:
- Otomasyon API'si tarafından desteklenmeyen özelliklerin otomatik olarak filtrelenmesi.
- Kullanıcılara, kısıtlama kullanan özellikler ve parametreler için geçerli bir değer seçme seçeneği sunma olanağı.