Bạn có thể truy cập vào Automation API thông qua Home API cho Android, nhưng vì điểm truy cập của các API này là thông qua một cấu trúc, nên trước tiên, bạn phải được cấp quyền đối với cấu trúc đó thì mới có thể sử dụng.
Sau khi được cấp quyền cho một cấu trúc, hãy nhập các gói này vào ứng dụng của bạn:
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.Id
import com.google.home.Structure
Một cấu trúc chứa giao diện HasAutomations với các phương thức dành riêng cho tính năng tự động hoá sau đây:
| API | Mô tả |
|---|---|
automations() |
Liệt kê tất cả quy trình tự động hoá thuộc nhà. Chỉ những quy trình tự động hoá mà bạn đã tạo thông qua Home API mới được trả về. |
createAutomation(automation) |
Tạo một thực thể tự động hoá cho một nhà. |
deleteAutomation(automationId) |
Xoá một thực thể tự động hoá theo mã nhận dạng. |
Tạo một quy trình tự động hoá
Sau khi tạo một thực thể của Home và nhận được quyền từ người dùng, hãy lấy cấu trúc và(các) thiết bị:
val structure = homeManager.structures().list().single()
val device = homeManager.devices().get(Id("myDevice"))!!
Sau đó, hãy xác định logic của quy trình tự động hoá bằng DSL tự động hoá. Trong Home API,
một quy trình tự động hoá được biểu thị bằng giao diện Automation. Giao diện này chứa một tập hợp các thuộc tính:
- Siêu dữ liệu, chẳng hạn như tên và nội dung mô tả.
- Cờ cho biết, ví dụ: liệu quy trình tự động hoá có thể được thực thi hay không.
- Danh sách các nút chứa logic của quy trình tự động hoá, được gọi là biểu đồ tự động hoá, được biểu thị bằng thuộc tính
automationGraph.
Theo mặc định, automationGraph thuộc loại SequentialFlow, là một lớp chứa danh sách các nút thực thi theo thứ tự tuần tự. Mỗi nút đại diện cho một phần tử của quy trình tự động hoá, chẳng hạn như điều kiện khởi động, điều kiện hoặc hành động.
Chỉ định name và description cho quy trình tự động hoá.
Khi tạo một quy trình tự động hoá, cờ isActive sẽ được đặt thành true theo mặc định. Do đó, bạn không cần phải đặt rõ ràng cờ này, trừ phi ban đầu bạn muốn tắt quy trình tự động hoá. Trong trường hợp đó, hãy đặt cờ thành false trong quá trình tạo.
Giao diện DraftAutomation được dùng để xây dựng và tạo quy trình tự động hoá, còn giao diện Automation được dùng để truy xuất. Ví dụ: sau đây là DSL tự động hoá cho một quy trình tự động hoá bật một thiết bị khi một thiết bị khác được bật:
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()) }
}
}
Sau khi xác định DSL tự động hoá, hãy truyền DSL đó đến phương thức createAutomation() để tạo thực thể DraftAutomation:
val createdAutomation = structure.createAutomation(automation)
Tại đây, bạn có thể sử dụng tất cả các phương thức tự động hoá khác cho quy trình tự động hoá, chẳng hạn như execute(), stop() và update().
Lỗi xác thực
Nếu quy trình tạo tự động hoá không vượt qua quy trình xác thực, thì một thông báo cảnh báo hoặc thông báo lỗi sẽ cung cấp thông tin về vấn đề. Để biết thêm thông tin, hãy tham khảo
tài liệu tham khảo về ValidationIssueType.
Ngay cả khi createAutomation() hoàn tất mà không gửi ngoại lệ, quy trình tự động hoá được tạo có thể không hợp lệ hoặc không thể thực thi. Phần phụ trợ cho phép lưu
bản nháp tự động hoá không hợp lệ (ví dụ: nếu người dùng chưa cấp
các quyền cần thiết như Phát hiện khuôn mặt quen thuộc hoặc nếu thiết bị thiếu các chức năng).
Bạn phải luôn xác minh isValid và kiểm tra validationIssues trên thực thể Automation được trả về:
val createdAutomation = structure.createAutomation(automation)
if (!createdAutomation.isValid) {
// Iterate through validation issues to identify errors and warnings
for (issue in createdAutomation.validationIssues) {
when (issue.severity) {
ValidationIssueSeverity.ERROR -> {
Log.e(
"Automation",
"Validation error on node ${issue.node}: ${issue.issueType}"
)
// Handle error (for example, prompt the user to enable missing
// consents or device features)
}
ValidationIssueSeverity.WARNING -> {
Log.w(
"Automation",
"Validation warning on node ${issue.node}: ${issue.issueType}"
)
}
else -> {}
}
}
}
Ví dụ về mã
Sau đây là một số mã ví dụ có thể dùng để triển khai các phần của quy trình tự động hoá giả định được mô tả trên trang Thiết kế quy trình tự động hoá trên Android.
Tự động hoá đơn giản
Một quy trình tự động kéo rèm lên lúc 8:00 sáng có thể được triển khai như sau:
// 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.
}
Tự động hoá phức tạp
Một quy trình tự động hoá kích hoạt đèn nhấp nháy khi phát hiện chuyển động có thể được triển khai như sau:
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())}
}
}
}
}
Chọn thiết bị một cách linh động bằng bộ lọc thực thể
Khi viết một quy trình tự động hoá, bạn không chỉ có thể chỉ định các thiết bị cụ thể. Tính năng bộ lọc thực thể cho phép quy trình tự động hoá của bạn chọn thiết bị trong thời gian chạy dựa trên nhiều tiêu chí.
Ví dụ: khi sử dụng bộ lọc thực thể, quy trình tự động hoá của bạn có thể nhắm đến:
- tất cả thiết bị thuộc một loại thiết bị cụ thể
- tất cả thiết bị trong một phòng cụ thể
- tất cả thiết bị thuộc một loại thiết bị cụ thể trong một phòng cụ thể
- tất cả thiết bị đang bật
- tất cả thiết bị đang bật trong một phòng cụ thể
Cách sử dụng bộ lọc pháp nhân:
- Trên
StructurehoặcRoom, hãy gọiatExecutionTime(). Thao tác này sẽ trả về mộtTypedExpression<TypedEntity<StructureType>>. - Trên đối tượng này, hãy gọi
getDevicesOfType(), truyền vào đó mộtDeviceType.
Bạn có thể dùng bộ lọc thực thể trong điều kiện khởi động, trình đọc trạng thái và hành động.
Ví dụ: để bất kỳ Đèn bật/tắt nào cũng có thể kích hoạt quy trình tự động hoá từ một điều kiện khởi động:
// If any light is turned on or off val starter = starter( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, )
Để ghi lại trạng thái OnOff của tất cả đèn trong một cấu trúc (cụ thể là đèn Bật/Tắt) trong trình đọc trạng thái:
// Build a Map<Entity, OnOff> val onOffStateOfAllLights = stateReader( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, )
Cách lấy đèn trong một phòng cụ thể và sử dụng đèn trong một điều kiện:
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 } }
Trong thời gian chạy:
| Trường hợp | Kết quả |
|---|---|
| Không có thiết bị nào đáp ứng tiêu chí trong một điều kiện khởi động. | Quy trình tự động hoá không kích hoạt. |
| Không có thiết bị nào đáp ứng tiêu chí trong trình đọc trạng thái. | Quy trình tự động hoá sẽ bắt đầu nhưng tiếp tục dựa trên nút điều kiện. |
| Không có thiết bị nào đáp ứng tiêu chí trong một hành động. | Quy trình tự động hoá bắt đầu nhưng hành động không làm gì cả. |
Ví dụ sau đây là một quy trình tự động hoá tắt tất cả đèn ngoại trừ đèn hành lang bất cứ khi nào một đèn riêng lẻ bị tắt:
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()) } } }
Thực thi quy trình tự động hoá
Chạy một quy trình tự động hoá đã tạo bằng phương thức execute():
createdAutomation.execute()
Nếu quy trình tự động hoá có một
điều kiện khởi động thủ công, thì execute()
sẽ bắt đầu quy trình tự động hoá từ điểm đó, bỏ qua tất cả các nút đứng trước
điều kiện khởi động thủ công. Nếu quy trình tự động hoá không có trình khởi động thủ công, thì quá trình thực thi sẽ bắt đầu từ nút theo sau nút khởi động đầu tiên.
Nếu thao tác execute() không thành công, HomeException có thể được gửi. Xem phần Xử lý lỗi.
Dừng quy trình tự động hoá
Dừng một quy trình tự động hoá đang chạy bằng phương thức stop():
createdAutomation.stop()
Nếu thao tác stop() không thành công, HomeException có thể được gửi. Xem phần Xử lý lỗi.
Lấy danh sách quy trình tự động hoá cho một nhà
Quy trình tự động hoá được xác định ở cấp nhà. Thu thập automations() của cấu trúc để truy cập vào Flow của quy trình tự động hoá:
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"))
}
}
Ngoài ra, hãy gán giá trị này cho một Collection cục bộ:
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()
Lấy quy trình tự động hoá theo mã nhận dạng
Để lấy một quy trình tự động theo mã quy trình tự động, hãy gọi phương thức
automations()
trên nhà và so khớp theo mã nhận dạng:
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()
Phản hồi:
// 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")
]))
Lấy quy trình tự động hoá theo tên
Bạn có thể dùng phương thức filter() trong Kotlin để tinh chỉnh thêm các lệnh gọi API. Để lấy một quy trình tự động hoá theo tên, hãy lấy các quy trình tự động hoá của nhà và lọc theo tên quy trình tự động hoá:
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") }
Xem tất cả quy trình tự động hoá cho một thiết bị
Để nhận tất cả các quy trình tự động hoá tham chiếu đến một thiết bị nhất định, hãy sử dụng tính năng lọc lồng nhau để quét automationGraph của từng quy trình tự động hoá:
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
}
}
}
Cập nhật quy trình tự động hoá
Để cập nhật siêu dữ liệu của một quy trình tự động hoá, hãy gọi phương thức update() của quy trình đó, truyền cho phương thức này một biểu thức lambda đặt siêu dữ liệu:
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" }
Phương thức update() hỗ trợ thay thế hoàn toàn biểu đồ tự động hoá, nhưng không hỗ trợ chỉnh sửa biểu đồ theo từng nút. Việc chỉnh sửa theo từng nút dễ xảy ra lỗi do các nút phụ thuộc lẫn nhau. Nếu
bạn muốn thay đổi logic của một quy trình tự động hoá, hãy tạo một biểu đồ mới và thay thế
hoàn toàn biểu đồ hiện có.
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")) }
}
}
}
Xoá quy trình tự động hoá
Để xoá một quy trình tự động, hãy sử dụng phương thức deleteAutomation() của cấu trúc. Bạn phải xoá quy trình tự động hoá bằng mã nhận dạng của quy trình đó.
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)
Nếu không xoá được, HomeException có thể được gửi. Xem phần Xử lý lỗi.
Tác động của việc xoá thiết bị đối với quy trình tự động hoá
Nếu người dùng xoá một thiết bị được dùng trong quy trình tự động hoá, thì thiết bị đã xoá đó không thể kích hoạt bất kỳ điều kiện khởi động nào và quy trình tự động hoá sẽ không thể đọc các thuộc tính từ thiết bị đó hoặc ra lệnh cho thiết bị đó. Ví dụ: nếu người dùng xoá một
OccupancySensorDevice
khỏi nhà của họ và một quy trình tự động hoá có một điều kiện khởi động phụ thuộc vào
OccupancySensorDevice đó, thì điều kiện khởi động đó không thể kích hoạt quy trình tự động hoá nữa.