Доступ к 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() | Перечислите все автоматизации, относящиеся к структуре. Возвращаются только автоматизации, созданные через Home API. |
createAutomation(automation) | Создайте экземпляр автоматизации для структуры. |
deleteAutomation(automationId) | Удалить экземпляр автоматизации по его идентификатору. |
Создать автоматизацию
После создания экземпляра Home и получения разрешений от пользователя получите структуру и устройство(а):
val structure = homeManager.structures().list().single()
val device = homeManager.devices().get(Id("myDevice"))!!
Затем определите логику вашей автоматизации с помощью Automation DSL. В Home API автоматизация представлена интерфейсом Automation
. Этот интерфейс содержит набор свойств:
- Метаданные, такие как имя и описание.
- Флаги, указывающие, например, можно ли выполнить автоматизацию.
- Список узлов, содержащих логику автоматизации, называемый графом автоматизации, представленный свойством
automationGraph
.
По умолчанию automationGraph
относится к типу SequentialFlow
— классу, содержащему список узлов, выполняемых последовательно. Каждый узел представляет собой элемент автоматизации, например, стартер, условие или действие.
Присвойте автоматизации name
и description
.
При создании автоматизации флаг isActive
по умолчанию устанавливается в true
, поэтому нет необходимости явно устанавливать этот флаг, если только вы изначально не хотите, чтобы автоматизация была отключена. В этом случае установите флаг в значение false
при создании.
Интерфейс DraftAutomation
используется для построения и создания автоматизаций, а интерфейс Automation
— для извлечения данных. Например, вот 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
автоматизаций используйте 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()
Получить автоматизацию по ID
Чтобы получить автоматизацию по идентификатору автоматизации, вызовите метод 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()
. Автоматизацию необходимо удалить, указав её идентификатор.
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
, этот пускатель больше не сможет активировать эту систему.