Geräte unter Android steuern

Prüfen, ob ein Merkmal einen Befehl unterstützt

Die Unterstützung kann auch für einen Trait-Befehl überprüft werden. Verwenden Sie außerdem die Trait-Level-Funktion supports, um zu prüfen, ob ein Befehl für ein bestimmtes Gerät unterstützt wird.

Um beispielsweise zu prüfen, ob ein Gerät den Befehl toggle des On/Off-Traits unterstützt:

// Check if the OnOff trait supports the toggle command.
if (onOffTrait.supports(OnOff.Command.Toggle)) {
  println("onOffTrait supports toggle command")
} else {
  println("onOffTrait does not support stateful toggle command")
}

Befehl an Geräte senden

Das Senden eines Befehls ähnelt dem Lesen eines Statusattributs aus einem Trait. Um das Gerät ein- oder auszuschalten, verwenden Sie den Toggle-Befehl des OnOff-Traits, der im Datenmodell des Google Home-Ökosystems als toggle() definiert ist. Mit dieser Methode wird onOff in false geändert, wenn es true ist, oder in true, wenn es false ist:

// Calling a command on a trait.
try {
  onOffTrait.toggle()
} catch (e: HomeException) {
  // Code for handling the exception
}

Alle Trait-Befehle sind suspend-Funktionen und werden erst abgeschlossen, wenn eine Antwort von der API zurückgegeben wird (z. B. eine Bestätigung, dass sich der Gerätestatus geändert hat). Befehle können eine Ausnahme zurückgeben, wenn ein Problem mit dem Ausführungsablauf erkannt wird. Als Entwickler sollten Sie einen try-catch-Block verwenden, um diese Ausnahmen ordnungsgemäß zu behandeln und den Benutzern detaillierte Informationen zu Fällen bereitzustellen, in denen die Fehler zu Maßnahmen führen. Unbehandelte Ausnahmen stoppen die Laufzeit der App und können zu Abstürzen führen.

Alternativ können Sie den Status explizit mit den Befehlen off() oder on() festlegen:

onOffTrait.off()
onOffTrait.on()

Nach dem Senden eines Befehls zur Statusänderung können Sie den Status nach dessen Abschluss wie unter Gerätestatus lesen beschrieben auslesen und in Ihrer App verarbeiten. Alternativ können Sie die unter Status beobachten beschriebenen Abläufe verwenden; dies ist die empfohlene Methode.

Sende einen Befehl mit Parametern

Manche Befehle verwenden möglicherweise Parameter, wie beispielsweise die der Merkmale OnOff oder LevelControl:

offWithEffect

// Turn off the light using the DyingLight effect.
onOffTrait.offWithEffect(
  effectIdentifier = OnOffTrait.EffectIdentifierEnum.DyingLight,
  effectVariant = 0u,
)

moveToLevel

// Change the brightness of the light to 50%
levelControlTrait.moveToLevel(
  level = 127u.toUByte(),
  transitionTime = null,
  optionsMask = LevelControlTrait.OptionsBitmap(),
  optionsOverride = LevelControlTrait.OptionsBitmap(),
)

Manche Befehle haben optionale Argumente, die nach den erforderlichen Argumenten angegeben werden.

Der step-Befehl für das FanControl-Attribut hat beispielsweise zwei optionale Argumente:

val fanControlTraitFlow: Flow<FanControl?> =
  device.type(FanDevice).map { it.standardTraits.fanControl }.distinctUntilChanged()

val fanControl = fanControlTraitFlow.firstOrNull()

// Calling a command with optional parameters not set.
fanControl?.step(direction = FanControlTrait.StepDirectionEnum.Increase)

// Calling a command with optional parameters.
fanControl?.step(direction = FanControlTrait.StepDirectionEnum.Increase) { wrap = true }

Prüfen, ob ein Attribut von einem Merkmal unterstützt wird

Einige Geräte unterstützen möglicherweise das Matter-Merkmal, aber nicht ein bestimmtes Attribut. Ein Cloud-to-cloud-Gerät, das einem Matter-Gerät zugeordnet wurde, unterstützt beispielsweise möglicherweise nicht alle Matter-Attribute. Verwenden Sie in solchen Fällen die Funktion supports auf Trait-Ebene und das Attribute-Enum des Traits, um zu prüfen, ob das Attribut für ein bestimmtes Gerät unterstützt wird.

So prüfen Sie beispielsweise, ob ein Gerät das Attribut onOff des Ein/Aus-Merkmals unterstützt:

// Check if the OnOff trait supports the onOff attribute.
if (onOffTrait.supports(OnOff.Attribute.onOff)) {
  println("onOffTrait supports onOff state")
} else {
  println("onOffTrait is for a command only device!")
}

Einige Attribute sind in der Matter-Spezifikation oder im Cloud-to-cloud smart home-Schema nullbar. Bei diesen Attributen können Sie mithilfe von isNullable zusätzlich zu supports feststellen, ob ein vom Attribut zurückgegebener null darauf zurückzuführen ist, dass das Gerät diesen Wert nicht meldet, oder ob der Wert des Attributs tatsächlich null ist:

// Check if a nullable attribute is set or is not supported.
if (onOffTrait.supports(OnOff.Attribute.startUpOnOff)) {
  // The device supports startupOnOff, it is safe to expect this value in the trait.
  if (OnOff.Attribute.startUpOnOff.isNullable && onOffTrait.startUpOnOff == null) {
    // This value is nullable and set to null. Check the specification as to
    // what null in this case means
    println("onOffTrait supports startUpOnOff and it is null")
  } else {
    // This value is nullable and set to a value.
    println("onOffTrait supports startUpOnOff and it is set to ${onOffTrait.startUpOnOff}")
  }
} else {
  println("onOffTrait does not support startUpOnOff!")
}

Eigenschaftsattribute aktualisieren

Wenn Sie den Wert eines bestimmten Attributs ändern möchten und keiner der Befehle des Traits dies tut, kann es sein, dass das Attribut das explizite Festlegen seines Werts unterstützt.

Ob der Wert eines Attributs geändert werden kann, hängt von zwei Faktoren ab:

  • Ist das Attribut beschreibbar?
  • Kann sich der Wert des Attributs als Nebeneffekt des Sendens eines Trait-Befehls ändern?

Diese Informationen finden sich in der Referenzdokumentation für Merkmale und deren Attribute.

Die Kombinationen von Eigenschaften, die bestimmen, wie der Wert eines Attributs geändert werden kann, sind also:

  • Schreibgeschützt und nicht von anderen Befehlen betroffen. Das bedeutet, dass sich der Wert des Attributs nicht ändert. Zum Beispiel das Attribut currentPosition des Merkmals Switch.

  • Schreibgeschützt und von anderen Befehlen betroffen. Das bedeutet, dass sich der Wert des Attributs nur durch das Senden eines Befehls ändern kann. Beispielsweise ist das Attribut currentLevel des Traits LevelControl Matter schreibgeschützt, sein Wert kann jedoch durch Befehle wie moveToLevel verändert werden.

  • Schreibbar und nicht von anderen Befehlen betroffen. Das bedeutet, dass Sie den Wert des Attributs direkt mit der update-Funktion des Traits ändern können. Es gibt jedoch keine Befehle, die sich auf den Wert des Attributs auswirken. Beispiel: das Attribut WrongCodeEntryLimit des Traits DoorLock.

  • Beschreibbar und beeinflusst durch andere Befehle. Das bedeutet, dass Sie den Wert des Attributs direkt mit der update-Funktion des Traits ändern können und sich der Wert des Attributs durch das Senden eines Befehls ändern kann. Das Attribut speedSetting des FanControlTrait kann beispielsweise direkt geschrieben werden, ist aber auch mit dem Befehl step änderbar.

Beispiel für die Verwendung der Update-Funktion zum Ändern des Werts eines Attributs

In diesem Beispiel wird gezeigt, wie der Wert des DoorLockTrait.WrongCodeEntryLimit-Attributs explizit festgelegt wird.

Um einen Attributwert festzulegen, rufen Sie die update-Funktion des Traits auf und übergeben Sie ihr eine Mutator-Funktion, die den neuen Wert festlegt. Es empfiehlt sich, zuerst zu prüfen, ob das Attribut vom Trait unterstützt wird.

Beispiel:

    val doorLockDevice = home.devices().list().first { device -> device.has(DoorLock) }

    val traitFlow: Flow<DoorLock?> =
      doorLockDevice.type(DoorLockDevice).map { it.standardTraits.doorLock }.distinctUntilChanged()

    val doorLockTrait: DoorLock = traitFlow.first()!!

    if (doorLockTrait.supports(DoorLock.Attribute.wrongCodeEntryLimit)) {
      val unused = doorLockTrait.update { setWrongCodeEntryLimit(3u) }
    }

Senden Sie mehrere Befehle gleichzeitig

Mit der Batching API kann ein Client mehrere Gerätebefehle der Home APIs in einer einzigen Nutzlast senden. Die Befehle werden in einer einzelnen Nutzlast zusammengefasst und parallel ausgeführt. Das ist vergleichbar mit der Erstellung einer Automatisierung für die Home API mit dem parallelen Knoten, wie im Beispiel Jalousien vor Sonnenaufgang öffnen. Die Batching API ermöglicht jedoch komplexere und anspruchsvollere Verhaltensweisen als die Automation API, z. B. die Möglichkeit, Geräte zur Laufzeit dynamisch nach beliebigen Kriterien auszuwählen.

Die Befehle in einem Batch können auf mehrere Merkmale auf mehreren Geräten, in mehreren Räumen und in mehreren Gebäuden ausgerichtet sein.

Wenn Befehle in einem Batch gesendet werden, können Geräte Aktionen gleichzeitig ausführen. Das ist nicht möglich, wenn Befehle sequenziell in separaten Anfragen gesendet werden. Mit Batchbefehlen kann der Entwickler den Status einer Gruppe von Geräten auf einen bestimmten aggregierten Status festlegen.

Batching API verwenden

Das Aufrufen von Befehlen über die Batching API umfasst drei grundlegende Schritte:

  1. Rufen Sie die Methode Home.sendBatchedCommands() auf.
  2. Geben Sie im Text des sendBatchedCommands()-Blocks die Befehle an, die im Batch enthalten sein sollen.
  3. Prüfen Sie die Ergebnisse der gesendeten Befehle, um festzustellen, ob sie erfolgreich waren oder fehlgeschlagen sind.

Rufen Sie die Methode sendBatchedCommands() auf.

Rufen Sie die Methode Home.sendBatchedCommands() auf. Im Hintergrund richtet diese Methode einen Lambda-Ausdruck in einem speziellen Batch-Kontext ein.

home.sendBatchedCommands() {

Batchbefehle angeben

Innerhalb des sendBatchedCommands()-Blocks werden batchierbare Befehle. eingefügt. Batchfähige Befehle sind „Schatten“-Versionen bestehender Device-API-Befehle, die in einem Batch-Kontext verwendet werden können und mit dem angehängten Suffix Batchable benannt werden. Zum Beispiel hat der Befehl moveToLevel() des Traits LevelControl ein Gegenstück mit der Bezeichnung moveToLevelBatchable().

Beispiel:

  val response1 = add(command1)

  val response2 = add(command2)

Der Batch wird automatisch gesendet, sobald alle Befehle dem Batch-Kontext hinzugefügt wurden und die Ausführung den Kontext verlassen hat.

Die Antworten werden in DeferredResponse<T>-Objekten erfasst.

Die DeferredResponse<T>-Instanzen können in einem Objekt eines beliebigen Typs zusammengefasst werden, z. B. in einem Collection oder einer von Ihnen definierten Datenklasse. Der Typ des Objekts, das Sie zum Zusammenstellen der Antworten auswählen, wird von sendBatchedCommands() zurückgegeben. Der Batchkontext kann beispielsweise zwei DeferredResponse-Instanzen in einem Pair zurückgeben:

  val (response1, response2) = homeClient.sendBatchedComamnds {
    val response1 = add(someCommandBatched(...))
    val response2 = add(someOtherCommandBatched(...))
    Pair(response1, response2)
  }

Alternativ kann der Batch-Kontext die DeferredResponse-Instanzen in einer benutzerdefinierten Datenklasse zurückgeben:

  // Custom data class
  data class SpecialResponseHolder(
    val response1: DeferredResponse<String>,
    val response2: DeferredResponse<Int>,
    val other: OtherResponses
  )
  data class OtherResponses(...)

Bitte prüfen Sie jede Antwort.

Prüfen Sie außerhalb des sendBatchedCommands()-Blocks die Antworten, um festzustellen, ob der entsprechende Befehl erfolgreich war oder fehlgeschlagen ist. Dies geschieht durch den Aufruf von DeferredResponse.getOrThrow(), der entweder: - das Ergebnis des ausgeführten Befehls zurückgibt, - oder, falls der Batch-Bereich nicht abgeschlossen wurde oder der Befehl nicht erfolgreich war, einen Fehler auslöst.

Sie sollten die Ergebnisse außerhalb des Lambda-Bereichs sendBatchedCommands() nur überprüfen.

Beispiel

Angenommen, Sie möchten eine App entwickeln, die mithilfe der Batching API eine „Gute Nacht“-Szene einrichtet, die alle Geräte im Haus für die Nacht konfiguriert, wenn alle schlafen. Diese App sollte das Licht ausschalten und die Vorder- und Hintertür verriegeln.

So können Sie vorgehen:

val lightDevices: List<OnOffLightDevice>
val doorlockDevices: List<DoorLockDevice>

// Send all the commands
val responses: List<DeferredResponse<Unit>> = home.sendBatchedCommands {
  // For each light device, send a Batchable command to turn it on
  val lightResponses: List<DeferredResponse<Unit>> = lightDevices.map { lightDevice ->
    add(lightDevice.standardTraits.onOff.onBatchable())
  }

  // For each doorlock device, send a Batchable command to lock it
  val doorLockResponse: List<DeferredResponse<Unit>> = doorlockDevices.map { doorlockDevice ->
    add(doorlockDevice.standardTraits.doorLock.lockDoorBatchable())
  }

  lightResponses + doorLockResponses
}

// Check that all responses were successful
for (response in responses) {
  response.getOrThrow()
}