Die Discovery API ist für Apps gedacht, mit denen Automatisierungen basierend auf den Geräten im Zuhause des Nutzers erstellt werden können. Sie kann einer App zur Laufzeit mitteilen, welche Merkmale und Geräte in einem bestimmten Gebäude vorhanden sind, um sie in Automatisierungen zu verwenden. Außerdem werden die zugehörigen Befehle, Attribute und Ereignisse sowie der Wertebereich angezeigt, der für Parameter und Felder zulässig ist.
Die Discovery API ignoriert alle Geräte oder Merkmale in einem Gebäude, die nicht von der Automation API unterstützt werden, sowie alle Geräte oder Merkmale, die nicht in FactoryRegistry
registriert wurden. Weitere Informationen zur Verwendung von FactoryRegistry
finden Sie unter Starter-Einrichtung.
API verwenden
Im Mittelpunkt der Discovery API steht die HasCandidates
-Schnittstelle, die die Wurzel einer Typhierarchie ist, zu der Structure
, Room
und HomeDevice
gehören.
Die Schnittstelle HasCandidates
definiert zwei Methoden, candidates()
und allCandidates()
, die beide Flow
-Objekte zurückgeben.
candidates()
generiert eine Liste mit Automatisierungskandidaten für die Entität (Structure
,Room
,HomeDevice
).allCandidates()
erstellt eine Liste mit Automatisierungskandidaten für das Element und alle untergeordneten Elemente. Diese Methode wird vonRoom
nicht unterstützt.
Im Gegensatz zu den anderen Flow
-Objekten, die von den Home APIs zurückgegeben werden, enthalten diese einen einmaligen Snapshot.
Um die aktuellste Liste der verfügbaren Kandidaten zu erhalten, muss der Entwickler jedes Mal candidates()
oder allCandidates()
aufrufen und kann nicht einfach collect()
auf die Flow
-Objekte anwenden. Da diese beiden Methoden besonders ressourcenintensiv sind, werden bei einem Aufruf häufiger als einmal pro Minute zwischengespeicherte Daten zurückgegeben, die möglicherweise nicht den aktuellen Status widerspiegeln.
Die Schnittstelle NodeCandidate
stellt einen Kandidatenknoten dar, der mit diesen beiden Methoden gefunden wurde. Sie ist die Wurzel einer Hierarchie, die die folgenden Schnittstellen enthält:
und die folgenden Klassen:
Mit Automatisierungskandidaten arbeiten
Angenommen, Sie entwickeln eine App, mit der eine Automatisierung erstellt wird, um eine Reihe von intelligenten Jalousien zu einer vom Nutzer angegebenen Zeit zu schließen. Sie wissen jedoch nicht, ob der Nutzer ein Gerät hat, das das WindowCovering
-Attribut unterstützt, und ob WindowCovering
oder eines seiner Attribute oder Befehle in Automatisierungen verwendet werden kann.
Im folgenden Code wird veranschaulicht, wie Sie mit der Discovery API die Ausgabe der Methode candidates()
filtern, um die Ergebnisse einzugrenzen und die gesuchte Art von Element (Struktur, Ereignis, Befehl) zu erhalten. Am Ende wird aus den erfassten Elementen eine Automatisierung erstellt.
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
Im folgenden Beispiel wird eine Automatisierung erstellt, um die Helligkeit einer Lampe einzustellen, wenn sie eingeschaltet wird.
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
Voraussetzungen prüfen
Die Discovery API informiert Sie darüber, dass für ein Attribut eine Voraussetzung für die Verwendung fehlt, z. B. ein Abo oder eine Gebäudeadresse.
Dazu wird das Attribut unsupportedReasons
der Klasse Candidate
verwendet. Dieses Attribut wird während des candidates()
-Aufrufs mit einer UnsupportedCandidateReason
ausgefüllt. Dieselben Informationen werden in den Validierungsfehlermeldungen angezeigt, wenn createAutomation()
aufgerufen wird.
Beispiele:
MissingStructureAddressSetup
informiert den Nutzer darüber, dass die Adresseinrichtung erforderlich ist, um das MerkmalTime
zu verwenden. Unter Google-Privatadresse ändern wird beschrieben, wie ein Nutzer die Adresse des Gebäudes mit der Google Home app (GHA) eingeben kann.MissingPresenceSensingSetup
informiert den Nutzer darüber, dass die Anwesenheitseinstellung erforderlich ist, um die MerkmaleAreaPresenceState
undAreaAttendanceState
zu verwenden.MissingSubscription
informiert den Nutzer darüber, dass ein Nest Aware-Abo erforderlich ist, um das AttributObjectDetection
zu verwenden.
Wenn Sie beispielsweise die MissingStructureAddressSetup
UnsupportedCandidateReason
verarbeiten möchten, können Sie in Ihrer App eine Toast-Mitteilung anzeigen und die GHA öffnen, damit der Nutzer die Adresse des Gebäudes angeben kann:
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(...)
}
Parameter validieren
Die Discovery API gibt die für ein Attribut, einen Parameter oder ein Ereignisfeld zulässigen Werte in Form einer Constraint
-Instanz zurück. So kann der App-Entwickler verhindern, dass Nutzer ungültige Werte festlegen.
Jede der Unterklassen von Constraint
hat eine eigene Methode, zulässige Werte darzustellen.
Einschränkungsklasse | Properties, die zulässige Werte darstellen |
---|---|
BitmapConstraint |
combinedBits
|
BooleanConstraint | |
ByteConstraint |
maxLength und minLength
|
EnumConstraint |
allowedSet
|
NumberRangeConstraint |
lowerBound , upperBound , step
und unit
|
NumberSetConstraint |
allowedSet und unit
|
StringConstraint |
allowedSet , disallowedSet , isCaseSensitive ,
maxLength , minLength und
regex
|
StructConstraint |
fieldConstraints
|
ListConstraint |
elementConstraint
|
Einschränkungen verwenden
Angenommen, Sie entwickeln eine App, die eine Automatisierung erstellt, mit der die Lautstärke eines Geräts mit dem Attribut LevelControl
festgelegt wird. Im folgenden Beispiel wird gezeigt, wie Sie dafür sorgen, dass der Wert, mit dem das Attribut currentLevel
des Merkmals LevelControl
festgelegt wurde, innerhalb der zulässigen Grenzen liegt.
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 und Discovery API vergleichen
Sie können Gerätetypen, Merkmale und ihre Attribute auch ohne Discovery API ermitteln. Mit der Devices API können Sie Folgendes ermitteln:
- Die primären Gerätetypen, für die der Nutzer dem Entwickler die Berechtigung zur Steuerung über die Methode
DeviceType.Metadata.isPrimaryType()
erteilt hat. - Ob jedes Gerät alle für die Automatisierung erforderlichen Eigenschaften unterstützt, mit der Methode
HasTraits.has()
- Prüfen Sie mit der Methode
supports()
, ob jedes Merkmal alle Attribute und Befehle unterstützt, die für die Automatisierung erforderlich sind.
Wenn Sie die Device API für die Gerätesuche verwenden, können Sie die folgenden Discovery API-Funktionen nicht nutzen:
- Automatische Filterung von Merkmalen, die von der Automation API nicht unterstützt werden.
- Die Möglichkeit, Nutzern die Auswahl eines gültigen Werts für Attribute und Parameter zu ermöglichen, für die Einschränkungen gelten.