Проверьте, поддерживает ли черта команду
Поддержка также может быть проверена для команды типажа. Также используйте функцию supports
на уровне характеристик, чтобы проверить, поддерживается ли команда для конкретного устройства.
Например, чтобы проверить, поддерживает ли устройство команду 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") }
Отправьте команду на устройство
Отправка команды аналогична чтению атрибута состояния из признака. Чтобы включить или выключить устройство, используйте команду Toggle свойства OnOff
, которая определена в модели данных экосистемы Google Home как toggle()
. Этот метод изменяет onOff
на false
, если оно имеет true
, или на true
если оно имеет false
:
// Calling a command on a trait. try { onOffTrait.toggle() } catch (e: HomeException) { // Code for handling the exception }
Все команды типажа являются функциями suspend
и завершаются только тогда, когда API возвращает ответ (например, подтверждение изменения состояния устройства). Команды могут возвращать исключение, если в потоке выполнения обнаружена проблема. Как разработчик, вы должны использовать блок try-catch
для правильной обработки этих исключений и предоставления пользователям подробной информации о случаях, когда ошибки требуют принятия мер. Необработанные исключения приведут к остановке выполнения приложения и могут привести к сбоям в работе вашего приложения.
Альтернативно, используйте команды off()
или on()
чтобы явно установить состояние:
onOffTrait.off() onOffTrait.on()
После отправки команды на изменение состояния после ее завершения вы можете прочитать состояние, как описано в разделе Чтение состояния устройства, чтобы обработать его в своем приложении. Альтернативно используйте потоки, как описано в разделе «Состояние наблюдения» , который является предпочтительным методом.
Отправить команду с параметрами
Некоторые команды могут использовать параметры, например, в свойствах OnOff
или LevelControl
:
OffWithEffect
// Turn off the light using the DyingLight effect. onOffTrait.offWithEffect( effectIdentifier = OnOffTrait.EffectIdentifierEnum.DyingLight, effectVariant = 0u, )
перемещениеToLevel
// Change the brightness of the light to 50% levelControlTrait.moveToLevel( level = 127u.toUByte(), transitionTime = null, optionsMask = LevelControlTrait.OptionsBitmap(), optionsOverride = LevelControlTrait.OptionsBitmap(), )
Некоторые команды имеют необязательные аргументы, которые идут после обязательных аргументов.
Например, команда step
для свойства FanControl
имеет два необязательных аргумента:
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 }
Проверьте, поддерживает ли черта атрибут
Некоторые устройства могут поддерживать особенность Matter , но не конкретный атрибут. Например, устройство Cloud-to-cloud , сопоставленное с Matter может не поддерживать все атрибуты Matter . Для обработки подобных случаев используйте функцию supports
на уровне признака и перечисление Attribute
признака, чтобы проверить, поддерживается ли атрибут для конкретного устройства.
Например, чтобы проверить поддержку устройством атрибута onOff
свойства On/Off:
// 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!") }
Некоторые атрибуты допускают значение null в спецификации Matter или схеме smart home Cloud-to-cloud . Для этих атрибутов вы можете определить, связано ли значение NULL , возвращаемое атрибутом, с тем, что устройство не сообщает об этом значении, или значение атрибута на самом деле равно null
, используя isNullable
в дополнение к 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!") }
Обновить атрибуты черт
Если вы хотите изменить значение данного атрибута, а ни одна из команд признака этого не делает, атрибут может поддерживать явную установку своего значения.
Возможность изменения значения атрибута зависит от двух факторов:
- Атрибут доступен для записи?
- Может ли значение атрибута измениться как побочный эффект отправки команды типажа?
Эту информацию предоставляет справочная документация по признакам и их атрибутам.
Таким образом, комбинации свойств, которые определяют, как может быть изменено значение атрибута:
Доступно только для чтения и не зависит от других команд. Это означает, что значение атрибута не меняется. Например, атрибут
currentPosition
чертыSwitch
.Доступно только для чтения и на него влияют другие команды. Это означает, что значение атрибута может измениться только в результате отправки команды. Например, атрибут
currentLevel
свойстваLevelControl
Matter доступен только для чтения, но его значение можно изменить с помощью таких команд, какmoveToLevel
.Доступен для записи и не зависит от других команд. Это означает, что вы можете напрямую изменить значение атрибута, используя функцию
update
признака, но нет команд, которые могли бы повлиять на значение атрибута. Например, атрибутWrongCodeEntryLimit
типажаDoorLock
.Доступен для записи и подвержен влиянию других команд. Это означает, что вы можете напрямую изменить значение атрибута, используя функцию
update
признака, а значение атрибута может измениться в результате отправки команды. Например, атрибутoccupiedCoolingSetpoint
свойстваThermostat
можно записать, но также обновить с помощью командыsetpointRaiseLower
.
Пример использования функции обновления для изменения значения атрибута
В этом примере показано, как явно задать значение атрибута DoorLockTrait.WrongCodeEntryLimit
.
Чтобы установить значение атрибута, вызовите функцию update
признака и передайте ему функцию-мутатор, которая устанавливает новое значение. Хорошей практикой является сначала убедиться, что признак поддерживает атрибут .
Например:
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) } }
Отправка нескольких команд одновременно
API пакетной обработки позволяет клиенту отправлять несколько команд устройства Home API в одной полезной нагрузке. Команды объединяются в одну полезную нагрузку и выполняются параллельно, аналогично тому, как можно создать автоматизацию Home API с использованием параллельного узла , например, в примере «Открыть жалюзи перед восходом солнца» . Однако API пакетной обработки обеспечивает более сложное и сложное поведение, чем API автоматизации, например возможность динамически выбирать устройства во время выполнения в соответствии с любыми критериями.
Команды в одном пакете могут быть нацелены на несколько характеристик на нескольких устройствах, в нескольких комнатах, в нескольких структурах.
Пакетная отправка команд позволяет устройствам выполнять действия одновременно, что невозможно, когда команды отправляются последовательно в отдельных запросах. Поведение, достигаемое с помощью пакетных команд, позволяет разработчику установить состояние группы устройств в соответствии с заранее определенным совокупным состоянием.
Используйте пакетный API
Вызов команд через API пакетной обработки состоит из трех основных шагов:
- Вызовите метод
Home.sendBatchedCommands()
. - В теле блока
sendBatchedCommands()
укажите команды, которые будут включены в пакет. - Проверьте результаты отправленных команд, чтобы увидеть, были ли они успешными или неудачными.
Вызовите метод sendBatchedCommands().
Вызовите метод Home.sendBatchedCommands()
. За кулисами этот метод устанавливает лямбда-выражение в специальном пакетном контексте.
home.sendBatchedCommands() {
Укажите пакетные команды
В теле блока sendBatchedCommands()
заполните пакетные команды. Пакетные команды представляют собой «теневые» версии существующих команд API устройства, которые можно использовать в пакетном контексте и к имени которых добавляется суффикс Batchable
. Например, команда moveToLevel()
свойства LevelControl
имеет аналог с именем moveToLevelBatchable()
.
Пример:
val response1 = add(command1)
val response2 = add(command2)
Пакет автоматически отправляется после того, как все команды были добавлены в контекст пакета и выполнение покинуло контекст.
Ответы фиксируются в объектах DeferredResponse<T>
.
Экземпляры DeferredResponse<T>
можно собрать в объект любого типа, например Collection
, или в определенный вами класс данных. Какой бы тип объекта вы ни выбрали для сборки ответов, именно его возвращает sendBatchedCommands()
. Например, пакетный контекст может возвращать два экземпляра DeferredResponse
в Pair
:
val (response1, response2) = homeClient.sendBatchedComamnds {
val response1 = add(someCommandBatched(...))
val response2 = add(someOtherCommandBatched(...))
Pair(response1, response2)
}
В качестве альтернативы пакетный контекст может возвращать экземпляры DeferredResponse
в пользовательском классе данных:
// Custom data class
data class SpecialResponseHolder(
val response1: DeferredResponse<String>,
val response2: DeferredResponse<Int>,
val other: OtherResponses
)
data class OtherResponses(...)
Проверьте каждый ответ
За пределами блока sendBatchedCommands()
проверьте ответы, чтобы определить, была ли соответствующая команда успешной или неудачной. Это делается путем вызова DeferredResponse.getOrThrow()
, который либо: — возвращает результат выполненной команды, — либо, если область действия пакета не завершена или команда оказалась неудачной, выдает ошибку.
Вам следует проверять результаты только за пределами лямбда-области sendBatchedCommands()
.
Пример
Допустим, вы хотите создать приложение, которое использует API пакетной обработки для настройки сцены «спокойной ночи», которая настраивает все устройства в доме на ночное время, когда все спят. Это приложение должно выключить свет и запереть передние и задние двери.
Вот один из способов решения этой задачи:
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()
}