Prüfen, ob ein Attribut einen Befehl unterstützt
Die Unterstützung kann auch für einen Attributbefehl geprüft werden. Verwende außerdem die Funktion supports auf Attributebene, um zu prüfen, ob ein Befehl für ein bestimmtes Gerät unterstützt wird.
So kannst du beispielsweise prüfen, ob ein Gerät den Befehl toggle des Attributs „An/Aus“ 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. Wenn du das Gerät ein- oder ausschalten möchtest, verwende den Toggle-Befehl des Attributs OnOff, der im Datenmodell des Google Home-Ökosystems als toggle() definiert ist. Mit dieser Methode wird onOff in false geändert, wenn der Wert true ist, oder in true, wenn der Wert false ist:
// Calling a command on a trait. try { onOffTrait.toggle() } catch (e: HomeException) { // Code for handling the exception }
Alle Befehle für Eigenschaften sind suspend-Funktionen und werden erst abgeschlossen, wenn die API eine Antwort zurückgibt (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 verarbeiten und Nutzern detaillierte Informationen zu Fällen zu liefern, in denen die Fehler behoben werden können. Unbehandelte Ausnahmen stoppen die App-Laufzeit und können zu Abstürzen in Ihrer App führen.
Alternativ können Sie den Status mit den Befehlen off() oder on() explizit festlegen:
onOffTrait.off() onOffTrait.on()
Nachdem du einen Befehl zum Ändern des Status gesendet hast, kannst du den Status wie unter Gerätestatus lesen beschrieben auslesen, um ihn in deiner App zu verarbeiten. Alternativ kannst du Abläufe wie unter Status beobachten beschrieben verwenden. Dies ist die bevorzugte Methode.
Befehl mit Parametern senden
Einige Befehle können Parameter verwenden, z. B. die in den Attributen 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 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 einer Eigenschaft unterstützt wird
Einige Geräte unterstützen möglicherweise ein Matter-Trait, aber kein bestimmtes Attribut. Beispielsweise unterstützt ein Cloud-to-cloud-Gerät, das Matter zugeordnet wurde, möglicherweise nicht alle Matter-Attribute. Verwende in solchen Fällen die Funktion supports auf Trait-Ebene und das Enum Attribute des Traits, um zu prüfen, ob das Attribut für ein bestimmtes Gerät unterstützt wird.
So kannst du beispielsweise prüfen, ob ein Gerät das Attribut onOff des Merkmals „An/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 können in der Matter-Spezifikation oder im Cloud-to-cloud-smart home-Schema null sein. Bei diesen Attributen können Sie feststellen, ob ein vom 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. Verwenden Sie dazu zusätzlich zu supports den Wert isNullable:
// 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 du den Wert eines bestimmten Attributs ändern möchtest und keiner der Befehle des Attributs 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 Nebenwirkung des Sendens eines Attributbefehls ändern?
Diese Informationen finden Sie in der Referenzdokumentation für Eigenschaften und ihre Attribute.
Daher sind die Kombinationen von Attributen, 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 Attribut
currentPositiondes MerkmalsSwitch.Schreibgeschützt und von anderen Befehlen betroffen. Das bedeutet, dass der Wert des Attributs nur durch das Senden eines Befehls geändert werden kann. Beispielsweise ist das Attribut
currentLeveldes MerkmalsLevelControlMatter schreibgeschützt, aber sein Wert kann durch Befehle wiemoveToLevelgeändert werden.Beschreibbar und nicht von anderen Befehlen betroffen. Das bedeutet, dass du den Wert des Attributs direkt mit der Funktion
updatedes Merkmals ändern kannst, es aber keine Befehle gibt, die sich auf den Wert des Attributs auswirken. Beispiel: das AttributWrongCodeEntryLimitdes MerkmalsDoorLock.Beschreibbar und von anderen Befehlen betroffen. Das bedeutet, dass du den Wert des Attributs direkt mit der Funktion
updatedes Merkmals ändern kannst. Außerdem kann sich der Wert des Attributs ändern, wenn ein Befehl gesendet wird. Beispielsweise kann das AttributspeedSettingvonFanControlTraitdirekt geschrieben werden, ist aber auch mit dem Befehlstepänderbar.
Beispiel für die Verwendung der Update-Funktion zum Ändern des Werts eines Attributs
In diesem Beispiel wird gezeigt, wie Sie den Wert des Attributs DoorLockTrait.WrongCodeEntryLimit explizit festlegen.
Wenn du einen Attributwert festlegen möchtest, rufe die update-Funktion des Traits auf und übergib ihr eine Mutatorfunktion, die den neuen Wert festlegt.
Es empfiehlt sich, zuerst zu prüfen, ob das Attribut vom Merkmal 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) } }
Mehrere Befehle gleichzeitig senden
Mit der Batching API kann ein Client mehrere Home APIs-Gerätebefehle in einer einzigen Nutzlast senden. Die Befehle werden in einer einzigen Nutzlast zusammengefasst und parallel ausgeführt, ähnlich wie bei einer Automatisierung der Home API mit dem parallelen Knoten, z. B. 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 nach beliebigen Kriterien dynamisch auszuwählen.
Die Befehle in einem Batch können auf mehrere Attribute auf mehreren Geräten, in mehreren Räumen und in mehreren Gebäuden abzielen.
Wenn Befehle in einem Batch gesendet werden, können Geräte Aktionen gleichzeitig ausführen. Das ist nicht möglich, wenn Befehle nacheinander in separaten Anfragen gesendet werden. Mit dem Verhalten, das durch Batchbefehle erreicht wird, kann der Entwickler den Status einer Gruppe von Geräten so festlegen, dass er einem vordefinierten 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 in den Batch aufgenommen werden sollen. - Prüfen Sie die Ergebnisse der gesendeten Befehle, um festzustellen, ob sie erfolgreich waren oder fehlgeschlagen sind.
Batchbefehle senden
Rufen Sie die Methode Home.sendBatchedCommands() auf. Im Hintergrund richtet diese Methode einen Lambda-Ausdruck in einem speziellen Batchkontext ein.
home.sendBatchedCommands() {
Batchbefehle angeben
Füllen Sie im Text des sendBatchedCommands()-Blocks batchfähige Befehle ein. Batchfähige Befehle sind „Schattenversionen“ vorhandener Device API-Befehle, die in einem Batchkontext verwendet werden können. Sie werden mit dem Suffix Batchable benannt. Beispielsweise hat der Befehl moveToLevel() des Attributs LevelControl ein Gegenstück namens 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. Unabhängig davon, welchen Objekttyp Sie zum Zusammenstellen der Antworten auswählen, wird dieser 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(...)
Antworten 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. Dadurch wird entweder:
- das Ergebnis des ausgeführten Befehls zurückgegeben,
- oder ein Fehler ausgegeben, wenn der Batchbereich nicht abgeschlossen wurde oder der Befehl nicht erfolgreich war.
Sie sollten die Ergebnisse nur außerhalb des sendBatchedCommands()-Lambda-Bereichs prüfen.
Beispiel
Angenommen, du möchtest eine App erstellen, die die Batching API verwendet, um eine „Gute Nacht“-Szene 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 die Aufgabe angehen:
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()
}
Gerätegruppe steuern
Zusätzlich zur detaillierten Steuerung jedes Mitglieds einer Gruppe durch Iterieren durch die Mitgliedsgeräte können Sie eine DeviceGroup als einzelne Entität steuern. DeviceGroup implementiert die Schnittstelle PartsProvider, die die von den Geräten in der Gruppe unterstützten DeviceType-Teile verfügbar macht:
group.parts()gibt eineFlow<Set<DeviceType>>aller Gerätetypen in der Gruppe zurück.group.part()undgroup.partOrNull()geben einen Ablauf für einen bestimmten Gerätetyp zurück, z. B.OnOffLightDeviceoderDimmableLightDevice.
Wenn du einen Trait-Befehl für einen Gerätetyp aufrufst, der Teil einer DeviceGroup ist, leitet das SDK den Befehl an alle zugehörigen Mitgliedsgeräte in der Gruppe weiter, die diese Funktion unterstützen, unabhängig von ihrer zugrunde liegenden Verbindung oder ihrem Transport (z. B. Matter oder Cloud-to-cloud).
Gruppenattribute für Teileigenschaften und Fehlerbehandlung
Die Ausrichtung auf einen Gruppenteil vereinfacht gängige Fälle, in denen Sie einen idempotenten Befehl (z. B. das Ein- oder Ausschalten von Lichtern oder das Festlegen einer Helligkeitsstufe) an eine Gruppe senden möchten. Beachte bei der Arbeit mit Attributen und Befehlen für einen DeviceGroup-Teil Folgendes:
- Dynamische Statusattribute (z. B. das Attribut
onOffdes AttributsOnOffoder das AttributcurrentLeveldes AttributsLevelControl) werden im Allgemeinen nicht für das Attribut eines Gruppenmitglieds ausgefüllt, da sich die Mitgliedsgeräte in einer Gruppe in verschiedenen Status befinden können, die nicht zu einem einzigen Wert kombiniert werden können. Unterstützte Befehle (mitsupports()geprüft, z. B.LevelControl.Command.MoveToLevelWithOnOffbei einer dimmbaren Lampe) und ausgewählte Funktionsattribute (z. B.colorTempPhysicalMinMiredsundcolorTempPhysicalMaxMiredsbeiColorControlfür farbige Lampen) werden jedoch ausgefüllt, damit deine App die UI-Steuerelemente entsprechend konfigurieren kann. - Nicht alle Befehle für Eigenschaften von Gruppenmitgliedern sind für einen Gruppenteil verfügbar. Insbesondere sind Befehle, die Werte zurückgeben, nicht verfügbar.
- Ein Befehl für einen Gruppenteil gibt keine einzelnen Ergebnisse für jeden zugrunde liegenden Befehl für ein Mitgliedsgerät zurück. Wenn Sie einzelne Befehlsergebnisse prüfen oder Befehle aufrufen möchten, die Werte zurückgeben, iterieren Sie direkt durch die Mitgliedsgeräte der Gruppe.
- Befehle, die an einen Teil eines
DeviceGroupgesendet werden, werden abgeschlossen, sobald das gesamte Fanout auf den Mitgliedsgeräten abgeschlossen ist und alle zugrunde liegenden Befehlserfolge oder -fehler erfasst wurden. Ein Befehl schlägt nur dann fehl (löst einenHomeExceptionaus), wenn er für jedes verknüpfte Mitgliedsgerät fehlschlägt oder wenn alle Zielgeräte nicht verfügbar sind. Dies wird als einzelner Befehlsfehler angezeigt, der der Syntax für den Aufruf eines einzelnen Befehls entspricht. Wenn mindestens ein Mitgliedsgerät den Befehl ausführt, wird der Gruppenbefehl ohne Ausnahme abgeschlossen.
Befehle an einen Teil einer Gruppe senden
So schaltest du alle OnOffLightDevice-Lampen in einer Gerätegruppe ein:
val lightPart = group.partOrNull(OnOffLightDevice).firstOrNull()
val onOffTrait = lightPart?.trait(OnOff)
if (onOffTrait?.supports(OnOff.Command.On) == true) {
onOffTrait.on()
}
Du kannst auch alle Gerätetypen in einer Gruppe mit group.parts() prüfen, um ein Merkmal auf mehrere verwandte Gerätetypen anzuwenden (z. B. alle Lichtgerätetypen in der Gruppe ausschalten, die OnOff unterstützen):
val lightTypes = listOf(
OnOffLightDevice,
DimmableLightDevice,
ColorTemperatureLightDevice,
ExtendedColorLightDevice,
)
val parts = group.parts().first()
val onOffTrait = lightTypes.firstNotNullOfOrNull { type ->
parts.firstOrNull { it.factory == type }
}?.trait(OnOff)
onOffTrait?.off()
Du kannst die Befehlsunterstützung prüfen oder die Funktionsgrenzen eines Gruppenparts abrufen, bevor du einen Befehl sendest. Du kannst beispielsweise MoveToLevelWithOnOff für einen dimmbaren Lichtpart prüfen oder colorTempPhysicalMinMireds und colorTempPhysicalMaxMireds für einen Farbtemperatur-Lichtpart abrufen:
val dimmableLight = group.partOrNull(DimmableLightDevice).firstOrNull()
val levelControl = dimmableLight?.trait(LevelControl)
if (
levelControl?.supports(
LevelControl.Command.MoveToLevelWithOnOff
) == true
) {
levelControl.moveToLevelWithOnOff(
level = 127u,
transitionTime = null,
optionsMask = LevelControlTrait.OptionsBitmap(),
optionsOverride = LevelControlTrait.OptionsBitmap(),
)
}
val colorLight = group.partOrNull(ColorTemperatureLightDevice).firstOrNull()
val colorControl = colorLight?.trait(ColorControl)
val minMireds = colorControl?.colorTempPhysicalMinMireds
val maxMireds = colorControl?.colorTempPhysicalMaxMireds
if (colorControl != null && minMireds != null && maxMireds != null) {
val midMireds = ((minMireds.toInt() + maxMireds.toInt()) / 2).toUShort()
colorControl.moveToColorTemperature(
colorTemperatureMireds = midMireds,
transitionTime = 0u,
optionsMask = ColorControlTrait.OptionsBitmap(),
optionsOverride = ColorControlTrait.OptionsBitmap(),
)
}