自动化 API 可通过适用于 Android 的 Home API 进行访问,但由于其入口点是通过结构,因此必须先授予对结构的权限,然后才能使用这些 API。
为结构授予权限后,将以下软件包导入到您的应用中:
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) |
按 ID 删除自动化实例。 |
创建自动化操作
创建 Home 实例并从用户处获得权限后,获取结构和设备:
val structure = homeManager.structures().list().single()
val device = homeManager.devices().get(Id("myDevice"))!!
然后,使用自动化 DSL 定义自动化的逻辑。在 Home API 中,自动化操作由 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
。详见错误处理。
获取结构的自动化操作列表
自动化操作是在结构级别定义的。收集结构中的 automations()
以访问 Flow
自动化功能:
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 获取自动化
如需按自动化 ID 获取自动化,请对结构调用 automations()
方法,并按 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().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")
]))
按名称获取自动化操作
Kotlin 中的 filter()
方法可用于进一步优化 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()
方法,并向其传递一个用于设置元数据的 lambda 表达式:
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
,那么该启动方式将无法再触发相应自动化操作。