Geräte unter Android steuern

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

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

So prüfen Sie beispielsweise, ob ein Gerät den Befehl des Merkmals „Ein/Aus“ unterstützt: toggle

// 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 Merkmal. Verwenden Sie den Befehl „Umschalten“ des OnOff Merkmals, der im Datenmodell des Google Home-Ökosystems als toggle() definiert ist, um das Gerät ein- oder auszuschalten. 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 Merkmalsbefehle 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 festgestellt wird. Als Entwickler sollten Sie einen try-catch-Block verwenden, um diese Ausnahmen ordnungsgemäß zu verarbeiten und Nutzern detaillierte Informationen zu Fällen zu geben, in denen die Fehler behoben werden können. Nicht behandelte Ausnahmen beenden die Laufzeit der App und können zu Abstürzen in Ihrer App führen.

Alternativ können Sie die Befehle off() oder on() verwenden, um den Status explizit festzulegen:

onOffTrait.off()
onOffTrait.on()

Nachdem Sie einen Befehl zum Ändern des Status gesendet haben, können Sie nach Abschluss den Status lesen, wie unter Gerätestatus lesen beschrieben, um ihn in Ihrer App zu verarbeiten. Alternativ können Sie Abläufe verwenden, wie unter Status beobachten beschrieben. Dies ist die bevorzugte Methode.

Befehl mit Parametern senden

Einige Befehle können Parameter verwenden, z. B. die Befehle der OnOff oder LevelControl Merkmale:

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(),
)

Einige Befehle haben optionale Argumente, die nach den erforderlichen Argumenten stehen.

Der Befehl step für das FanControl Merkmal 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 Merkmal ein Attribut unterstützt

Einige Geräte unterstützen möglicherweise ein Matter Merkmal, aber nicht ein bestimmtes Attribut. Ein Cloud-to-cloud Gerät, das Matter zugeordnet wurde, unterstützt beispielsweise möglicherweise nicht alle Matter Attribute. Verwenden Sie in solchen Fällen die Funktion supports auf Merkmalsebene und die Enumeration Attribute des Merkmals, 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 Merkmals „Ein/Aus“ 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 dem Cloud-to-cloud smart home Schema nullable. Für diese Attribute können Sie mit isNullable zusätzlich zu supports feststellen, ob ein null-Wert, der vom Attribut zurückgegeben wird, 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!")
}

Merkmalsattribute aktualisieren

Wenn Sie den Wert eines bestimmten Attributs ändern möchten und keiner der Befehle des Merkmals dies tut, unterstützt das Attribut möglicherweise das explizite Festlegen des Werts.

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 Merkmalsbefehls ändern?

Diese Informationen finden Sie in der Referenzdokumentation für Merkmale und ihre Attribute.

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

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

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

  • Beschreibbar und nicht von anderen Befehlen betroffen. Das bedeutet, dass Sie den Wert des Attributs direkt mit der Funktion update des Merkmals ändern können, aber es gibt keine Befehle, die sich auf den Wert des Attributs auswirken. Beispiel: Das WrongCodeEntryLimit Attribut des DoorLock Merkmals.

  • Beschreibbar und von anderen Befehlen betroffen. Das bedeutet, dass Sie den Wert des Attributs direkt mit der Funktion update des Merkmals ändern können und sich der Wert des Attributs durch das Senden eines Befehls ändern kann. Das speedSetting Attribut des FanControlTrait kann beispielsweise direkt beschrieben werden, ist aber auch mit dem step Befehl änderbar.

Beispiel für die Verwendung der Funktion „update“ zum Ändern des Werts eines Attributs

In diesem Beispiel wird gezeigt, wie Sie den Wert des DoorLockTrait.WrongCodeEntryLimit Attributs explizit festlegen.

Rufen Sie zum Festlegen eines Attributwerts die Funktion update des Merkmals auf und übergeben Sie ihr eine Mutator-Funktion, die den neuen Wert festlegt. Es empfiehlt sich, zuerst zu prüfen, ob das Merkmal ein Attribut unterstützt.

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) }
    }

Mehrere Befehle gleichzeitig senden

Mit der Batching API kann ein Client mehrere Gerätebefehle der Home API in einer einzelnen Nutzlast senden. Die Befehle werden in einer einzelnen Nutzlast zusammengefasst und parallel ausgeführt, ähnlich wie bei der Erstellung einer Home API Automatisierung mit dem parallelen Knoten, z. B. im Beispiel Jalousien vor Sonnenaufgang öffnen . Die Batching API ermöglicht jedoch komplexere und ausgefeiltere 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 in mehreren Strukturen 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 dem Verhalten, das mit Batch-Befehlen erzielt wird, kann der Entwickler den Status einer Gruppe von Geräten so festlegen, dass er einem vordefinierten aggregierten Status entspricht.

Batching API verwenden

Es gibt drei grundlegende Schritte zum Aufrufen von Befehlen über die Batching API:

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

Methode „sendBatchedCommands()“ aufrufen

Rufen Sie die Home.sendBatchedCommands() Methode auf. Im Hintergrund wird mit dieser Methode ein Lambda-Ausdruck in einem speziellen Batch-Kontext eingerichtet.

home.sendBatchedCommands() {

Batch-Befehle angeben

Füllen Sie im Text des Blocks sendBatchedCommands() die Batch-fähigen Befehle aus. Batch-fähige Befehle sind „Schattenversionen“ vorhandener Device API-Befehle, die in einem Batch-Kontext verwendet werden können. Sie werden mit dem zusätzlichen Suffix Batchable benannt. Der LevelControl des Merkmals moveToLevel() hat beispielsweise ein Gegenstück namens 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.

Antworten werden in DeferredResponse<T> Objekten erfasst.

Die DeferredResponse<T> Instanzen können in einem Objekt eines beliebigen Typs zusammengefasst werden, z. B. in einer 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 Batch-Kontext 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(...)

Jede Antwort prüfen

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

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

Beispiel

Angenommen, Sie möchten eine App entwickeln, die die Batching API verwendet, um eine „Gute Nacht“-Szene einzurichten, mit der alle Geräte im Zuhause für die Nacht konfiguriert werden, wenn alle schlafen. Diese App soll 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()
}