您可以透過 Android 適用的 Home API 存取 Automation API,但由於這些 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"))!!
然後使用 Automation 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 點升起百葉窗的自動化動作 可能像這樣實作:
// 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
,該啟動條件就無法再啟動自動化動作。