Prüfen, ob ein Merkmal einen Befehl unterstützt
Der Support kann auch für einen Attributbefehl geprüft werden. Verwende auch die Funktion supports
auf Trait-Ebene, 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 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 Merkmal. Verwende zum Ein- oder Ausschalten des Geräts 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 richtig zu verarbeiten und Nutzern detaillierte Informationen zu Fällen zu präsentieren, in denen die Fehler behoben werden können. Nicht abgefangene Ausnahmen beenden die App-Laufzeit und können zu Abstürzen in Ihrer App führen.
Alternativ können Sie den Status explizit mit den Befehlen off()
oder on()
festlegen:
onOffTrait.off() onOffTrait.on()
Nachdem Sie einen Befehl zum Ändern des Status gesendet haben, können Sie den Status nach Abschluss des Befehls lesen, wie unter Gerätestatus lesen beschrieben, um ihn in Ihrer App zu verarbeiten. Alternativ können Sie Flows verwenden, wie unter Status beobachten beschrieben. Dies ist die bevorzugte Methode.
Befehl mit Parametern senden
Einige Befehle verwenden möglicherweise Parameter, z. B. die Attribute 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(), )
Einige Befehle haben optionale Argumente, die nach den erforderlichen Argumenten stehen.
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 ein Matter-Merkmal, aber kein bestimmtes Attribut. Beispielsweise unterstützt ein Cloud-to-cloud-Gerät, das Matter zugeordnet wurde, möglicherweise nicht jedes Matter-Attribut. Um solche Fälle zu verarbeiten, verwenden Sie 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 Traits „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 im Cloud-to-cloud-smart home-Schema nullable. Bei diesen Attributen können Sie mithilfe von isNullable
zusätzlich zu supports
feststellen, ob ein von dem Attribut zurückgegebener Nullwert 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!") }
Trait-Attribute aktualisieren
Wenn Sie den Wert eines bestimmten Attributs ändern möchten und keiner der Befehle des Traits dies tut, kann der Wert des Attributs möglicherweise explizit festgelegt werden.
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 Sie in der Referenzdokumentation für Traits und ihre 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. Beispiel: das Attribut
currentPosition
des TraitsSwitch
.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
desLevelControl
-Matter-Traits ist beispielsweise schreibgeschützt, sein Wert kann aber durch Befehle wiemoveToLevel
geä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 AttributWrongCodeEntryLimit
des TraitsDoorLock
.Schreibbar und von anderen Befehlen betroffen. 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 AttributspeedSetting
desFanControlTrait
kann beispielsweise direkt geschrieben werden, ist aber auch mit dem Befehlstep
veränderbar.
Beispiel für die Verwendung der Update-Funktion zum Ändern des Werts eines Attributs
In diesem Beispiel wird gezeigt, wie der Wert des Attributs DoorLockTrait.WrongCodeEntryLimit
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 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 APIs in einer einzigen Nutzlast senden. Die Befehle werden in einer einzelnen Nutzlast zusammengefasst und parallel ausgeführt. Das ist ähnlich wie bei 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 so festlegen, dass er einem vorgegebenen aggregierten Status entspricht.
Batching API verwenden
Das Aufrufen von Befehlen über die Batching API umfasst drei grundlegende Schritte:
- Rufen Sie die Methode
Home.sendBatchedCommands()
auf. - Geben Sie im Text des
sendBatchedCommands()
-Blocks die Befehle an, die im Batch enthalten sein sollen. - Prüfen Sie die Ergebnisse der gesendeten Befehle, um festzustellen, ob sie erfolgreich waren oder fehlgeschlagen sind.
Methode „sendBatchedCommands()“ aufrufen
Rufen Sie die Methode Home.sendBatchedCommands()
auf. Hinter den Kulissen wird mit dieser Methode ein Lambda-Ausdruck in einem speziellen Batchkontext eingerichtet.
home.sendBatchedCommands() {
Batchbefehle angeben
Fügen Sie im Text des sendBatchedCommands()
-Blocks Batch-fähige Befehle ein. Batchfähige Befehle sind „Schatten“-Versionen vorhandener Device API-Befehle, die in einem Batchkontext verwendet werden können. Sie haben den zusätzlichen Suffix Batchable
. Der Befehl moveToLevel()
des Trait LevelControl
hat beispielsweise ein Gegenstück mit dem Namen moveToLevelBatchable()
.
Beispiel:
val response1 = add(command1)
val response2 = add(command2)
Der Batch wird automatisch gesendet, sobald alle Befehle dem Batchkontext 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 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 Batchkontext 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 sendBatchedCommands()
-Blocks die Antworten, um festzustellen, ob der entsprechende Befehl erfolgreich war oder fehlgeschlagen ist. Dazu wird DeferredResponse.getOrThrow()
aufgerufen, was entweder:
– das Ergebnis des ausgeführten Befehls zurückgibt,
– oder, wenn der Batchbereich nicht abgeschlossen wurde oder der Befehl nicht erfolgreich war, einen Fehler auslöst.
Sie sollten die Ergebnisse nur außerhalb des sendBatchedCommands()
-Lambdabereichs prüfen.
Beispiel
Angenommen, Sie möchten eine App entwickeln, die die Batching API verwendet, um eine Szene für die Nacht einzurichten, die alle Geräte im Zuhause für die Nacht konfiguriert, 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()
}