Sprawdzanie, czy cecha obsługuje polecenie
Możesz też sprawdzić, czy cecha obsługuje polecenie. Użyj też funkcji supports na poziomie cechy, aby sprawdzić, czy polecenie jest obsługiwane na danym urządzeniu.
Aby na przykład sprawdzić, czy urządzenie obsługuje polecenie cechy On/Off:
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") }
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
OnOff
polecenia Toggle cechy, które jest zdefiniowane 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 cech są suspend funkcjami i kończą się tylko wtedy, gdy interfejs API zwróci odpowiedź (np. potwierdzającą zmianę stanu urządzenia).
Polecenia mogą zwracać wyjątek, jeśli podczas wykonywania wystąpi problem. Jako programista musisz użyć bloku try-catch, aby prawidłowo obsługiwać te wyjątki i wyświetlać użytkownikom szczegółowe informacje w przypadkach, gdy błędy można naprawić. Nieobsłużone 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 i jego wykonaniu możesz odczytać stan zgodnie z opisem w sekcji Odczytywanie stanu urządzenia, aby obsługiwać go w aplikacji. Możesz też użyć przepływów zgodnie z opisem w sekcji Obserwowanie stanu, która jest preferowaną metodą.
Wysyłanie polecenia z parametrami
Niektóre polecenia mogą używać parametrów, np. tych z
OnOff lub
LevelControl
cech:
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 cechy FanControl
trait
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
zmapowane na Matter, może nie obsługiwać wszystkich
Matter atrybutów. Aby obsługiwać takie przypadki, użyj funkcji supports na poziomie cechy i wyliczenia Attribute cechy, aby sprawdzić, czy atrybut jest obsługiwany na danym urządzeniu.
Aby na przykład sprawdzić, czy urządzenie obsługuje atrybut cechy On/Off:
onOff
// 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ą mieć wartość null w specyfikacji Matter lub
w schemacie Cloud-to-cloud smart home. W przypadku tych atrybutów możesz określić, czy wartość null zwrócona przez atrybut jest spowodowana tym, że urządzenie nie zgłasza tej wartości, czy też wartość atrybutu jest rzeczywiście równa null. Aby to zrobić, użyj funkcji isNullable oprócz funkcji 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 cechy
Jeśli chcesz zmienić wartość danego atrybutu, a żadne z poleceń cechy tego nie robi, atrybut może obsługiwać jawne ustawienie jego 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ć jako efekt uboczny wysłania polecenia cechy?
Te informacje znajdziesz w dokumentacji referencyjnej dotyczącej cech i ich atrybutów.
Dlatego kombinacje właściwości, które określają, jak można zmienić wartość atrybutu, to:
Tylko do odczytu i nie mające wpływu na inne polecenia. Oznacza to, że wartość atrybutu nie zmienia się. Na przykład atry
currentPositionbut atrybutuSwitchcechy.Tylko do odczytu i mające wpływ na inne polecenia. Oznacza to, że wartość atrybutu można zmienić tylko w wyniku wysłania polecenia. Na przykład atrybut
currentLevelcechyLevelControlMatter trait jest tylko do odczytu, ale jego wartość można zmienić za pomocą poleceń takich jakmoveToLevel.Zapisywalne i nie mające wpływu na inne polecenia. Oznacza to, że możesz bezpośrednio zmienić wartość atrybutu za pomocą funkcji
updatecechy, ale nie ma poleceń, które wpłyną na wartość atrybutu. Na przykład atrybutWrongCodeEntryLimitcechyDoorLocktrait.Zapisywalne i mające wpływ na inne polecenia. Oznacza to, że możesz bezpośrednio zmienić wartość atrybutu za pomocą funkcji
updatecechy, a wartość atrybutu może się zmienić w wyniku wysłania polecenia. Na przykład atrybutspeedSettingFanControlTraitmożna bezpośrednio zapisać, ale można go też zmienić za pomocą poleceniastep.
Przykład użycia funkcji update do zmiany wartości atrybutu
Ten przykład pokazuje, jak jawnie ustawić wartość atrybutu
DoorLockTrait.WrongCodeEntryLimit atrybutu.
Aby ustawić wartość atrybutu, wywołaj funkcję update cechy i przekaż jej funkcję mutatora, która ustawia nową wartość.
Dobrym rozwiązaniem jest najpierw
sprawdzenie, czy cecha obsługuje atrybut.
Na przykład:
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) } }
Wysyłanie kilku poleceń naraz
Interfejs Batching API umożliwia klientowi wysyłanie wielu poleceń do interfejsów Home API w jednym ładunku. Polecenia są grupowane w jeden ładunek i wykonywane równolegle, podobnie jak w przypadku tworzenia automatyzacji Home API automatyzacji za pomocą węzła równoległego, np. w przykładzie Otwieranie rolet przed wschodem słońca . Interfejs Batching API umożliwia jednak bardziej złożone i zaawansowane zachowania niż interfejs Automation API, np. możliwość dynamicznego wybierania urządzeń w czasie działania według dowolnych kryteriów.
Polecenia w jednej partii mogą być kierowane na wiele cech na różnych urządzeniach, w różnych pokojach i w różnych strukturach.
Wysyłanie poleceń w partii umożliwia urządzeniom wykonywanie działań jednocześnie, co nie jest możliwe, gdy polecenia są wysyłane sekwencyjnie w osobnych żądaniach. Zachowanie osiągane za pomocą poleceń w partii umożliwia programiście ustawienie stanu grupy urządzeń tak, aby odpowiadał on z góry określonym stanom zbiorczym.
Korzystanie z interfejsu Batching API
Aby wywołać polecenia za pomocą interfejsu Batching API, musisz wykonać 3 podstawowe czynności:
- Wywołaj
Home.sendBatchedCommands()metodę. - W treści bloku
sendBatchedCommands()określ polecenia, które mają zostać uwzględnione w partii. - Sprawdź wyniki wysłanych poleceń, aby zobaczyć, czy zostały wykonane pomyślnie.
Wywoływanie metody sendBatchedCommands()
Wywołaj metodę
Home.sendBatchedCommands(). Za kulisami ta metoda konfiguruje wyrażenie lambda w specjalnym kontekście partii.
home.sendBatchedCommands() {
Określanie poleceń partii
W treści bloku sendBatchedCommands() wypełnij polecenia w partii. Polecenia w partii to „cienie” istniejących poleceń Device API, których można używać w kontekście partii. Ich nazwy mają dodany sufiks Batchable. Na przykład
cechy
ma odpowiednik o nazwie
moveToLevelBatchable().LevelControlmoveToLevel()
Przykład:
val response1 = add(command1)
val response2 = add(command2)
Partia jest wysyłana automatycznie, gdy wszystkie polecenia zostaną dodane do kontekstu partii, a wykonanie opuści ten kontekst.
Odpowiedzi są przechwytywane w
DeferredResponse<T>
obiektach.
Instancje DeferredResponse<T>
można zebrać w obiekt dowolnego typu, np.
Collection, lub zdefiniowaną przez Ciebie klasę danych. Typ obiektu, w którym zdecydujesz się zebrać odpowiedzi, jest zwracany przez sendBatchedCommands(). Na przykład kontekst partii może zwrócić 2
DeferredResponse instancje w Pair:
val (response1, response2) = homeClient.sendBatchedComamnds {
val response1 = add(someCommandBatched(...))
val response2 = add(someOtherCommandBatched(...))
Pair(response1, response2)
}
Alternatywnie, kontekst partii może zwrócić DeferredResponse
instancje w niestandardowej klasie danych:
// 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 odpowiednie polecenie zostało wykonane pomyślnie. Aby to zrobić, wywołaj
DeferredResponse.getOrThrow(), które albo:
- zwraca wynik wykonanego polecenia,
- albo, jeśli zakres partii nie został ukończony lub polecenie się
nie powiodło, zgłasza błąd.
Wyniki należy sprawdzać tylko poza zakresem lambda sendBatchedCommands().
Przykład
Załóżmy, że chcesz utworzyć aplikację, która używa interfejsu Batching API do skonfigurowania sceny „dobranoc”, która konfiguruje wszystkie urządzenia w domu na noc, gdy wszyscy śpią. Ta aplikacja powinna wyłączyć światła i zamknąć drzwi wejściowe i tylne.
Oto jeden ze sposobów podejścia do 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()
}