Доступ к API автоматизации можно получить через API Home для Android, но поскольку точка входа осуществляется через структуру, перед их использованием необходимо сначала предоставить разрешение этой структуре.
После предоставления разрешений для структуры импортируйте следующие пакеты в ваше приложение:
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.Id
import com.google.home.Structure
Структура содержит интерфейс HasAutomations со следующими методами, специфичными для автоматизации:
| API | Описание |
|---|---|
automations() | Перечислите все автоматизации, относящиеся к данной структуре. Возвращаются только те автоматизации, которые вы создали с помощью API Home. |
createAutomation(automation) | Создайте экземпляр автоматизации для структуры. |
deleteAutomation(automationId) | Удалите экземпляр автоматизации по его идентификатору. |
Создайте автоматизацию
После создания экземпляра Home и получения разрешений от пользователя, получите структуру и устройство(а):
val structure = homeManager.structures().list().single()
val device = homeManager.devices().get(Id("myDevice"))!!
Затем определите логику вашей автоматизации, используя Automation DSL. В API Home автоматизация представлена интерфейсом Automation . Этот интерфейс содержит набор свойств:
- Метаданные, такие как название и описание.
- Флаги, указывающие, например, на возможность выполнения автоматизации.
- Список узлов, содержащих логику автоматизации, называемый графом автоматизации, представлен свойством
automationGraph.
По умолчанию automationGraph имеет тип SequentialFlow , который представляет собой класс, содержащий список узлов, выполняющихся в последовательном порядке. Каждый узел представляет собой элемент автоматизации, такой как инициатор, условие или действие.
Присвойте автоматизации name и description .
При создании автоматизации флаг isActive по умолчанию устанавливается в true , поэтому нет необходимости явно устанавливать этот флаг, если вы изначально не хотите отключить автоматизацию. В этом случае установите флаг в false при создании.
Интерфейс DraftAutomation используется для создания и построения автоматизаций, а интерфейс Automation — для их получения. Например, вот DSL для автоматизации, которая включает одно устройство, когда включается другое:
import com.google.home.automation.Action
import com.google.home.automation.Automation
import com.google.home.automation.Condition
import com.google.home.automation.DraftAutomation
import com.google.home.automation.Equals
import com.google.home.automation.Node
import com.google.home.automation.SequentialFlow
import com.google.home.automation.Starter
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.matter.standard.OnOff
import com.google.home.Structure
...
val automation: DraftAutomation = automation {
name = "MyFirstAutomation"
description = "Turn on a device when another device is turned on."
sequential {
val starterNode = starter<_>(device1, OnOffLightDevice, trait=OnOff)
condition() { expression = stateReaderNode.onOff equals true }
action(device2, OnOffLightDevice) { command(OnOff.on()) }
}
}
После определения DSL для автоматизации передайте его методу createAutomation() для создания экземпляра DraftAutomation :
val createdAutomation = structure.createAutomation(automation)
Отсюда вы можете использовать все остальные методы автоматизации, такие как execute() , stop() и update() .
Ошибки проверки
Если автоматизация не проходит проверку, появляется предупреждение или сообщение об ошибке, содержащее информацию о проблеме. Для получения дополнительной информации обратитесь к справочнику ValidationIssueType .
Примеры кода
Здесь мы приводим пример кода, который можно использовать для реализации частей гипотетических автоматизаций, описанных на странице «Разработка автоматизации на Android» .
Простая автоматизация
Автоматическая система, поднимающая жалюзи в 8:00 утра, может быть реализована следующим образом:
// get all the automation node candidates in the structure
val allCandidates = structure.allCandidates().first()
// determine whether a scheduled automation can be constructed
val isSchedulingSupported =
allCandidates.any {
it is EventCandidate &&
it.eventFactory == Time.ScheduledTimeEvent &&
it.unsupportedReasons.isEmpty()
}
// get the blinds present in the structure
val blinds =
allCandidates
.filter {
it is CommandCandidate &&
it.commandDescriptor == WindowCoveringTrait.UpOrOpenCommand &&
it.unsupportedReasons.isEmpty()
}
.map { it.entity }
.filterIsInstance<HomeDevice>()
.filter { it.has(WindowCoveringDevice) }
if (isSchedulingSupported && blinds.isNotEmpty()) {
// Proceed to create automation
val automation: DraftAutomation = automation {
name = "Day time open blinds"
description = "Open all blinds at 8AM everyday"
isActive = true
sequential {
// At 8:00am local time....
val unused =
starter(structure, Time.ScheduledTimeEvent) {
parameter(Time.ScheduledTimeEvent.clockTime(LocalTime.of(8, 0, 0, 0)))
}
// ...open all the blinds
parallel {
for (blind in blinds) {
action(blind, WindowCoveringDevice) { command(WindowCovering.upOrOpen()) }
}
}
}
}
val createdAutomation = structure.createAutomation(automation)
} else if (!isSchedulingSupported) {
// Cannot create automation.
// Set up your address on the structure, then try again.
} else {
// You don't have any WindowCoveringDevices.
// Try again after adding some blinds to your structure.
}
Сложная автоматизация
Автоматизация, которая включает мигание света при обнаружении движения, может быть реализована следующим образом:
import com.google.home.Home
import com.google.home.HomeClient
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
import com.google.home.automation.action
import com.google.home.automation.automation
import com.google.home.automation.equals
import com.google.home.automation.parallel
import com.google.home.automation.starter
import com.google.home.google.AssistantBroadcast
import com.google.home.matter.standard.OnOff
import com.google.home.matter.standard.OnOff.Companion.toggle
import com.google.home.matter.standard.OnOffLightDevice
import java.time.Duration
// get all the automation node candidates in the structure
val allCandidates = structure.allCandidates().first()
// get the lights present in the structure
val availableLights = allCandidates.filter {
it is CommandCandidate &&
it.commandDescriptor == OnOffTrait.OnCommand
}.map { it.entity }
.filterIsInstance<HomeDevice>()
.filter {it.has(OnOffLightDevice) ||
it.has(ColorTemperatureLightDevice) ||
it.has(DimmableLightDevice) ||
it.has(ExtendedColorLightDevice)}
val selectedLights = ... // user selects one or more lights from availableLights
automation {
isActive = true
sequential {
// If the presence state changes...
val starterNode = starter<_>(structure, AreaPresenceState)
// ...and if the area is occupied...
condition() {
expression = starterNode.presenceState equals PresenceState.PresenceStateOccupied
}
// "blink" the light(s)
parallel {
for(light in selectedLights) {
action(light, OnOffLightDevice) { command(OnOff.toggle()) }
delayFor(Duration.ofSeconds(1))
action(light, OnOffLightDevice) { command(OnOff.toggle()) }
delayFor(Duration.ofSeconds(1))
action(light, OnOffLightDevice) { command(OnOff.toggle()) }
delayFor(Duration.ofSeconds(1))
action(light, OnOffLightDevice) { command(OnOff.toggle())}
}
}
}
}
Динамический выбор устройств с использованием фильтров сущностей.
При создании автоматизации вы не ограничены указанием конкретных устройств. Функция, называемая фильтрами сущностей, позволяет вашей автоматизации выбирать устройства во время выполнения на основе различных критериев.
Например, используя фильтры сущностей, ваша автоматизация может быть нацелена на следующие цели:
- все устройства определенного типа
- все устройства в конкретной комнате
- все устройства определенного типа в определенной комнате
- все включенные устройства
- все устройства, включенные в конкретной комнате
Для использования фильтров сущностей:
- Для
StructureилиRoomвызовите методatExecutionTime(). Он вернетTypedExpression<TypedEntity<StructureType>>. - Для этого объекта вызовите метод
getDevicesOfType(), передав емуDeviceType).
Фильтры сущностей могут использоваться в начальных строках, считывателях состояний и действиях.
Например, чтобы включение/выключение любого источника света запускало автоматизацию из стартового меню:
// If any light is turned on or off val starter = starter( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, )
Для того чтобы считыватель состояния мог фиксировать состояние OnOff всех светильников в здании (в частности, включенных/выключенных):
// Build a Map<Entity, OnOff> val onOffStateOfAllLights = stateReader( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, )
Чтобы настроить освещение в определенной комнате и использовать его в определенных условиях:
val livingRoomLights = stateReader( entityExpression = livingRoom.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, ) // Are any of the lights in the living room on? condition { expression = livingRoomLights.values.any { it.onOff equals true } }
Во время выполнения программы:
| Сценарий | Результат |
|---|---|
| Ни одно устройство не соответствует критериям для стартового комплекта. | Автоматизация не срабатывает. |
| В государственных считывающих устройствах ни одно из них не соответствует критериям. | Автоматизация запускается и будет продолжаться в зависимости от состояния узла. |
| Ни одно устройство не соответствует критериям, указанным в действии. | Автоматизация запускается, но действие ничего не делает. |
Следующий пример демонстрирует автоматизацию, которая выключает все светильники, кроме света в коридоре, всякий раз, когда выключается отдельный светильник:
val unused = automation { sequential { // If any light is turned on or off val starter = starter( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, ) condition { // Check to see if the triggering light was turned off expression = starter.onOff equals false } // Turn off all lights except the hall light action( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice).filter { it notEquals entity(hallwayLight, OnOffLightDevice) } ) { command(OnOff.on()) } } }
Запустить автоматизацию
Запустите созданную автоматизацию, используя метод execute() :
createdAutomation.execute()
Если автоматизация имеет ручной запуск , execute() запускает автоматизацию с этой точки, игнорируя все узлы, предшествующие ручному запуску. Если автоматизация не имеет ручного запуска, выполнение начинается с узла, следующего за первым узлом запуска.
Если операция execute() завершится неудачей, может быть выброшено исключение HomeException . См. раздел «Обработка ошибок» .
Остановить автоматизацию
Остановить запущенную автоматизацию можно с помощью метода stop() :
createdAutomation.stop()
Если операция stop() завершится неудачей, может быть выброшено исключение HomeException . См. раздел «Обработка ошибок» .
Получите список автоматизированных процессов для структуры.
Автоматизация определяется на уровне структуры. Для доступа к Flow автоматизации используйте функцию `collect` для объекта ` automations() структуры:
import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
...
val structure = homeManager.structures().list().single()
structure.automations().collect {
println("Available automations:")
for (automation in it) {
println(String.format("%S %S", "$automation.id", "$automation.name"))
}
}
В качестве альтернативы, его можно присвоить локальной Collection :
import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
...
var myAutomations: Collection<Automation> = emptyList()
myAutomations = structure.automations()
Получите автоматизацию по идентификатору
Чтобы получить автоматизацию по идентификатору, вызовите метод automations() для структуры и выполните поиск по идентификатору:
import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
...
val structure = homeManager.structures().list().single()
val automation: DraftAutomation = structure.automations().mapNotNull {
it.firstOrNull
{ automation -> automation.id == Id("automation-id") }
}.firstOrNull()
Ответ:
// Here's how the automation looks like in the get response.
// Here, it's represented as if calling a println(automation.toString())
Automation(
name = "automation-name",
description = "automation-description",
isActive = true,
id = Id("automation@automation-id"),
automationGraph = SequentialFlow(
nodes = [
Starter(
entity="device@test-device",
type="home.matter.0000.types.0101",
trait="OnOff@6789..."),
Action(
entity="device@test-device",
type="home.matter.0000.types.0101",
trait="OnOff@8765...",
command="on")
]))
Получите автоматизацию по имени.
Метод filter() в Kotlin можно использовать для дальнейшего уточнения вызовов API. Чтобы получить автоматизацию по имени, получите автоматизации структуры и отфильтруйте их по имени автоматизации:
import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
...
val structure = homeManager.structures().list().single()
val automation: DraftAutomation = structure.automations().filter {
it.name.equals("Sunset Blinds") }
Получить все автоматизации для устройства
Чтобы получить все автоматизации, которые ссылаются на данное устройство, используйте вложенную фильтрацию для сканирования automationGraph каждой автоматизации:
import android.util.Log
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
import com.google.home.automation.Action
import com.google.home.automation.Automation
import com.google.home.automation.Automation.automationGraph
import com.google.home.automation.Node
import com.google.home.automation.ParallelFlow
import com.google.home.automation.SelectFlow
import com.google.home.automation.SequentialFlow
import com.google.home.automation.Starter
import com.google.home.automation.StateReader
...
fun collectDescendants(node: Node): List<Node> {
val d: MutableList<Node> = mutableListOf(node)
val children: List<Node> =
when (node) {
is SequentialFlow -> node.nodes
is ParallelFlow -> node.nodes
is SelectFlow -> node.nodes
else -> emptyList()
}
for (c in children) {
d += collectDescendants(c)
}
return d
}
val myDeviceId = "device@452f78ce8-0143-84a-7e32-1d99ab54c83a"
val structure = homeManager.structures().list().single()
val automations =
structure.automations().first().filter {
automation: Automation ->
collectDescendants(automation.automationGraph!!).any { node: Node ->
when (node) {
is Starter -> node.entity.id.id == myDeviceId
is StateReader -> node.entity.id.id == myDeviceId
is Action -> node.entity.id.id == myDeviceId
else -> false
}
}
}
Обновить автоматизацию
Чтобы обновить метаданные автоматизации, вызовите её метод update() `, передав ему лямбда-выражение, устанавливающее метаданные:
import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
...
val structure = homeManager.structures().list().single()
val automation: DraftAutomation = structure.automations().mapNotNull {
it.firstOrNull
{ automation -> automation.id == Id("automation-id") }
}.firstOrNull()
automation.update { this.name = "Flashing lights 2" }
Метод update() поддерживает полную замену графа автоматизации, но не редактирование графа для отдельных узлов. Редактирование для отдельных узлов чревато ошибками из-за взаимозависимости узлов. Если вы хотите изменить логику автоматизации, создайте новый граф и полностью замените существующий.
import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
...
val structure = homeManager.structures().list().single()
val automation: Automation = structure.automations().mapNotNull {
it.firstOrNull
{ automation -> automation.id == Id("automation-id") }
}.firstOrNull()
automation.update {
this.automationGraph = sequential {
val laundryWasherCompletionEvent =
starter<_>(laundryWasher, LaundryWasherDevice, OperationCompletionEvent)
condition {
expression =
laundryWasherCompletionEvent.completionErrorCode equals
// UByte 0x00u means NoError
0x00u
}
action(speaker, SpeakerDevice) { command(AssistantBroadcast.broadcast("laundry is done")) }
}
}
}
Удалить автоматизацию
Для удаления автоматизации используйте метод deleteAutomation() структуры. Автоматизацию необходимо удалять, используя её идентификатор (ID).
import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
...
val structure = homeManager.structures().list().single()
val automation: DraftAutomation = structure.automations().first()
structure.deleteAutomation(automation.id)
Если удаление не удастся, может быть сгенерировано исключение HomeException . См. раздел «Обработка ошибок» .
Влияние удаления устройств на автоматизацию
Если пользователь удаляет устройство, используемое в автоматизации, удаленное устройство не сможет запускать никакие пусковые механизмы, и автоматизация не сможет считывать с него атрибуты или отдавать ему команды. Например, если пользователь удаляет устройство с OccupancySensorDevice из своего дома, и в автоматизации есть пусковой механизм, зависящий от этого OccupancySensorDevice , то этот пусковой механизм больше не сможет активировать автоматизацию.