Geräteerkennung nutzen

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 von Room 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 Merkmal Time 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 Merkmale AreaPresenceState und AreaAttendanceState zu verwenden.
  • MissingSubscription informiert den Nutzer darüber, dass ein Nest Aware-Abo erforderlich ist, um das Attribut ObjectDetection 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.

Tabelle: Arten von Constraint
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:

  1. Die primären Gerätetypen, für die der Nutzer dem Entwickler die Berechtigung zur Steuerung über die Methode DeviceType.Metadata.isPrimaryType() erteilt hat.
  2. Ob jedes Gerät alle für die Automatisierung erforderlichen Eigenschaften unterstützt, mit der Methode HasTraits.has()
  3. 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.