Sprawdzanie, czy cecha obsługuje polecenie
Możesz też sprawdzić, czy zespół pomocy nie ma poleceń dotyczących cech. Aby sprawdzić, czy polecenie jest obsługiwane na danym urządzeniu, użyj też funkcji supports
na poziomie atrybutu.
Aby na przykład sprawdzić, czy urządzenie obsługuje polecenie toggle
atrybutu Włącz/Wyłącz:
// 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") }
Wysyłanie polecenia do urządzenia
Wysyłanie polecenia jest podobne do odczytywania atrybutu stanu z cechy. Aby włączyć lub wyłączyć urządzenie, użyj polecenia przełącznika OnOff
w ramach funkcji sterowania, która jest zdefiniowana w modelu danych ekosystemu Google Home jako toggle()
. Ta metoda zmienia wartość onOff
na false
, jeśli jest ona równa true
, lub na true
, jeśli jest równa false
:
// Calling a command on a trait. try { onOffTrait.toggle() } catch (e: HomeException) { // Code for handling the exception }
Wszystkie polecenia dotyczące cech to funkcje suspend
, które są wykonywane tylko wtedy, gdy interfejs API zwróci odpowiedź (np. potwierdzenie, że stan urządzenia się zmienił).
Jeśli zostanie wykryty problem z przepływem wykonywania, polecenia mogą zwrócić wyjątek. Jako deweloper powinieneś używać bloku try-catch
, aby odpowiednio obsługiwać te wyjątki, oraz wyświetlać użytkownikom szczegółowe informacje o przypadkach, w których błędy można naprawić. Nieprzetworzone wyjątki zatrzymają działanie aplikacji i mogą spowodować jej awarię.
Możesz też użyć poleceń off()
lub on()
, aby jednoznacznie ustawić stan:
onOffTrait.off() onOffTrait.on()
Po wysłaniu polecenia zmiany stanu możesz odczytać stan urządzenia, korzystając z opisu w sekcji Odczyt stanu urządzenia, aby obsłużyć go w aplikacji. Możesz też użyć przepływów opisanych w sekcji Obserwowanie stanu, która jest preferowaną metodą.
Wysyłanie polecenia z parametrami
Niektóre polecenia mogą używać parametrów, takich jak te na stronie OnOff
lub LevelControl
właściwości:
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(), )
Niektóre polecenia mają argumenty opcjonalne, które występują po argumentach wymaganych.
Na przykład polecenie step
dotyczące atrybutu FanControl
ma 2 argumenty opcjonalne:
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 }
Sprawdzanie, czy cecha obsługuje atrybut
Niektóre urządzenia mogą obsługiwać cechę Matter, ale nie konkretny atrybut. Na przykład urządzenie Cloud-to-cloud, które zostało przypisane do Matter, może nie obsługiwać wszystkich atrybutów Matter. Aby obsłużyć takie przypadki, użyj funkcji supports
na poziomie cechy i enumeracji Attribute
tej cechy, aby sprawdzić, czy atrybut jest obsługiwany na danym urządzeniu.
Aby na przykład sprawdzić, czy urządzenie obsługuje atrybut onOff
cechy Włącz/wyłącz:
// 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!") }
Niektóre atrybuty mogą być puste w specyfikacji Matter lub w schemacie Cloud-to-cloud smart home. W przypadku tych atrybutów możesz określić, czy zwrócony przez atrybut null jest spowodowany tym, że urządzenie nie przekazuje tej wartości, czy też wartość atrybutu to rzeczywiście null
. Aby to sprawdzić, użyj atrybutu isNullable
zamiast atrybutu supports
:
// 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!") }
Aktualizowanie atrybutów cech
Jeśli chcesz zmienić wartość danego atrybutu, a żaden z poleceń atrybutu tego nie robi, atrybut może obsługiwać jawne ustawianie wartości.
Możliwość zmiany wartości atrybutu zależy od 2 czynników:
- Czy atrybut jest zapisywalny?
- Czy wartość atrybutu może się zmienić w efekcie ubocznym wysłania polecenia dotyczącego atrybutu?
Informacje te znajdziesz w dokumentacji referencyjnej dotyczącej cech i ich atrybutów.
Dlatego kombinacje właściwości, które określają, jak może się zmienić wartość atrybutu, to:
Do odczytu i nie podlega innym poleceniom. Oznacza to, że wartość atrybutu się nie zmienia. Na przykład atrybut
currentPosition
cechySwitch
.Tylko do odczytu i modyfikowane przez inne polecenia. Oznacza to, że wartość atrybutu może się zmienić tylko w wyniku wysłania polecenia. Na przykład atrybut
currentLevel
atrybutuLevelControl
Matter jest dostępny tylko do odczytu, ale jego wartość może być modyfikowana przez takie polecenia jakmoveToLevel
.Możliwość zapisu i brak wpływu na inne polecenia. Oznacza to, że możesz bezpośrednio zmienić wartość atrybutu, używając funkcji
update
cechy, ale nie ma żadnych poleceń, które mogłyby wpłynąć na wartość atrybutu. Na przykład atrybutWrongCodeEntryLimit
cechyDoorLock
.Do zapisu i modyfikowania przez inne polecenia. Oznacza to, że możesz bezpośrednio zmienić wartość atrybutu, używając funkcji
update
atrybutu, a wartość atrybutu może się zmienić w wyniku wysłania polecenia. Na przykład atrybutoccupiedCoolingSetpoint
cechyThermostat
można zapisać, ale też zaktualizować za pomocą poleceniasetpointRaiseLower
.
Przykład użycia funkcji update do zmiany wartości atrybutu
Ten przykład pokazuje, jak jawnie ustawić wartość atrybutu DoorLockTrait.WrongCodeEntryLimit
.
Aby ustawić wartość atrybutu, wywołaj funkcję update
atrybutu i podaj jej funkcję modyfikującą, która ustawi nową wartość.
Warto najpierw sprawdzić, czy cecha obsługuje atrybut.
Na przykład:
var 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) } }
Wysyłanie wielu poleceń jednocześnie
Interfejs Batching API umożliwia klientowi wysyłanie wielu poleceń interfejsu Home API w jednym ładunku. Polecenia są grupowane w jedną grupę i wykonywane równolegle, podobnie jak w przypadku automatyzacji interfejsu Home API za pomocą węzła równoległego, na przykład w przypadku otwierania rolet przed wschodem słońca. Interfejs Batching API umożliwia jednak bardziej złożone i zaawansowane zachowania niż interfejs Automation API, np. dynamiczne wybieranie urządzeń w czasie wykonywania kodu zgodnie z dowolnymi kryteriami.
Polecenia w jednym pakiecie mogą być kierowane na wiele cech na wielu urządzeniach, w wielu pokojach i w wielu strukturach.
Wysyłanie poleceń zbiorczo umożliwia urządzeniom wykonywanie działań jednocześnie, co nie jest możliwe, gdy polecenia są wysyłane sekwencyjnie w osobnych żądaniach. Za pomocą poleceń zbiorczych deweloper może ustawić stan grupy urządzeń zgodnie z wstępnie określonym stanem zbiorczym.
Korzystanie z interfejsu Batching API
Aby wywołać polecenia za pomocą interfejsu API grupowania, musisz wykonać 3 podstawowe czynności:
- Wywołaj metodę
Home.sendBatchedCommands()
. - W treści bloku
sendBatchedCommands()
podaj polecenia, które mają zostać uwzględnione w zbiorze. - Sprawdź wyniki wysłanych poleceń, aby zobaczyć, czy zostały wykonane pomyślnie, czy nie.
Wywołaj metodę sendBatchedCommands().
Wywołaj metodę Home.sendBatchedCommands()
. Pod spodem ta metoda konfiguruje wyrażenie lambda w specjalnym kontekście zbiorczego przetwarzania.
home.sendBatchedCommands() {
Określanie poleceń zbiorczych
W treści bloku sendBatchedCommands()
wpisz polecenia do wykonywania w grupach. Polecenia zbiorcze to „cienie” dotychczasowych poleceń interfejsu Device API, które można używać w kontekście zbiorczym. Ich nazwy zawierają sufiks Batchable
. Na przykład polecenie LevelControl
w przypadku atrybutu moveToLevel()
ma odpowiednik o nazwie moveToLevelBatchable()
.
Przykład:
val response1 = add(command1)
val response2 = add(command2)
Zamówienie jest wysyłane automatycznie, gdy wszystkie polecenia zostaną dodane do kontekstu zbiorczego, a wykonanie zakończy działanie w tym kontekście.
Odpowiedzi są rejestrowane w obiektach DeferredResponse<T>
.
DeferredResponse<T>
instancje mogą być zbierane w obiekcie dowolnego typu, np. Collection
lub zdefiniowanej przez Ciebie klasie danych. sendBatchedCommands()
zwraca obiekt o tym typie, który wybierzesz do złożenia odpowiedzi. Na przykład kontekst zbiorczego może zwrócić 2 instancje DeferredResponse
w ramach Pair
:
val (response1, response2) = homeClient.sendBatchedComamnds {
val response1 = add(someCommandBatched(...))
val response2 = add(someOtherCommandBatched(...))
Pair(response1, response2)
}
Może też zwracać instancje DeferredResponse
w klasie danych niestandardowych:
// Custom data class
data class SpecialResponseHolder(
val response1: DeferredResponse<String>,
val response2: DeferredResponse<Int>,
val other: OtherResponses
)
data class OtherResponses(...)
Sprawdzanie każdej odpowiedzi
Poza blokiem sendBatchedCommands()
sprawdź odpowiedzi, aby określić, czy polecenie zostało wykonane, czy nie. Aby to zrobić, wywołaj funkcję DeferredResponse.getOrThrow()
, która:
- zwraca wynik wykonanego polecenia,
- lub, jeśli zakres zbiorczego przetwarzania nie został ukończony lub polecenie nie powiodło się, zgłasza błąd.
Wyniki należy sprawdzać tylko poza zakresem funkcji sendBatchedCommands()
lambda.
Przykład
Załóżmy, że chcesz utworzyć aplikację, która używa interfejsu Batching API do konfigurowania sceny „dobranoc”, która konfiguruje wszystkie urządzenia w domu na czas nocy, gdy wszyscy śpią. Aplikacja powinna wyłączyć światła i zamknąć drzwi wejściowe i tylne.
Oto jeden z możliwych sposobów wykonania tego zadania:
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()
}