คุณอาจเข้าถึง Automation API ผ่าน Home 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) |
ลบอินสแตนซ์การทำงานอัตโนมัติตามรหัส |
สร้างการทำงานอัตโนมัติ
หลังจากสร้างอินสแตนซ์ของ Home และได้รับสิทธิ์จากผู้ใช้แล้ว ให้รับโครงสร้างและอุปกรณ์ดังนี้
val structure = homeManager.structures().list().single()
val device = homeManager.devices().get(Id("myDevice"))!!
จากนั้นกําหนดตรรกะของการทํางานอัตโนมัติโดยใช้ Automation DSL ใน Home API การทำงานอัตโนมัติจะแสดงโดยอินเทอร์เฟซ Automation
อินเทอร์เฟซนี้มีชุดพร็อพเพอร์ตี้ต่อไปนี้
- ข้อมูลเมตา เช่น ชื่อและคำอธิบาย
- แฟล็กที่ระบุ เช่น การทำงานอัตโนมัติจะทํางานได้หรือไม่
- รายการโหนดที่มีตรรกะของการทํางานอัตโนมัติ ซึ่งเรียกว่ากราฟการทำงานอัตโนมัติที่แสดงโดยพร็อพเพอร์ตี้
automationGraph
โดยค่าเริ่มต้น automationGraph
จะเป็นประเภท SequentialFlow
ซึ่งเป็นคลาสที่มีรายการโหนดที่ดำเนินการตามลําดับ โดยแต่ละโหนดจะแสดงถึงองค์ประกอบของการทำงานอัตโนมัติ เช่น เงื่อนไขเริ่มต้น เงื่อนไข หรือการดำเนินการ
กำหนด name
และ description
ให้กับการทำงานอัตโนมัติ
การสร้างการทำงานอัตโนมัติจะตั้งค่า Flag isActive
เป็น true
โดยค่าเริ่มต้น คุณจึงไม่จำเป็นต้องตั้งค่า Flag นี้อย่างชัดเจน เว้นแต่ว่าคุณต้องการปิดใช้การทำงานอัตโนมัติตั้งแต่แรก ในกรณีนี้ ให้ตั้งค่า Flag เป็น 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 การทำงานอัตโนมัติแล้ว ให้ส่ง DSL นั้นไปยังเมธอด createAutomation()
เพื่อสร้างอินสแตนซ์ DraftAutomation
val createdAutomation = structure.createAutomation(automation)
จากตรงนี้ คุณจะใช้วิธีการทํางานอัตโนมัติอื่นๆ ทั้งหมดในระบบอัตโนมัติได้ เช่น execute()
, stop()
และ update()
ข้อผิดพลาดจากการตรวจสอบ
หากการสร้างการทำงานอัตโนมัติไม่ผ่านการตรวจสอบ ข้อความเตือนหรือข้อผิดพลาดจะแสดงข้อมูลเกี่ยวกับปัญหา ดูข้อมูลเพิ่มเติมได้ที่ValidationIssueType
reference
ตัวอย่างโค้ด
ต่อไปนี้เป็นตัวอย่างโค้ดบางส่วนที่อาจใช้เพื่อติดตั้งใช้งานการทํางานอัตโนมัติสมมติบางส่วนที่อธิบายไว้ในหน้าออกแบบการทำงานอัตโนมัติ
การทำงานอัตโนมัติแบบง่าย
การทำงานอัตโนมัติที่เปิดม่านเวลา 08: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 API, Device API และ Discovery API เพื่อติดตั้งใช้งานแอปที่แนะนำผู้ใช้ในการสร้างการทำงานอัตโนมัติทั่วไปของตนเอง (ตามที่อธิบายไว้ในออกแบบการทำงานอัตโนมัติ: การทำงานอัตโนมัติทั่วไป)
val structureFlow: Flow<Structure> = home.structures().itemFlow(myStructureId)
// Get a snapshot of the structure.
val structure: Structure = structureFlow.first()
// map of devices where key is device id
val devicesById = mutableMapOf<Id,HomeDevice>()
home.devices().list().map { devicesById.put(it.id, it) }
// map of rooms where key is room id
val roomsByName: MutableMap<String,Room?> = mutableMapOf<String,Room?>()
structure.rooms().list().map { roomsByName.put(it.name, it) }
// map of commands where key is trait id
val commandsByTrait: MutableMap<String,MutableList<CommandCandidate>?> = mutableMapOf<String,MutableList<CommandCandidate>?>()
// map of commands where key is DeviceType.Metadata.hashcode()
val commandsByDeviceType: MutableMap<Id,MutableList<CommandCandidate>?> = mutableMapOf<Id,MutableList<CommandCandidate>?>()
// map of commands where key is entity id
val commandsByEntity: MutableMap<Id,MutableList<CommandCandidate>?> = mutableMapOf<Id,MutableList<CommandCandidate>?>()
// map of stateReaders where key is trait id
val stateReadersByTrait: MutableMap<Int,MutableList<StateReaderCandidate>?> = mutableMapOf<Int,MutableList<StateReaderCandidate>?>()
// map of stateReaders where key is DeviceType.Metadata.hashcode()
val stateReadersByDeviceType: MutableMap<Int,MutableList<StateReaderCandidate>?> = mutableMapOf<Int,MutableList<StateReaderCandidate>?>()
// map of stateReaders where key is entity id
val stateReadersByEntity: MutableMap<Int,MutableList<StateReaderCandidate>?> = mutableMapOf<Int,MutableList<StateReaderCandidate>?>()
// map of starters where key is trait id
val startersByTrait: MutableMap<Int,MutableList<StarterCandidate>?> = mutableMapOf<Int,MutableList<StarterCandidate>?>()
// map of starters where key is DeviceType.Metadata.hashcode()
val startersByDeviceType: MutableMap<Int,MutableList<CommandCandidate>?> = mutableMapOf<Int,MutableList<CommandCandidate>?>()
// map of starters where key is entity id
val startersByEntity: MutableMap<Int,MutableList<StarterCandidate>?> = mutableMapOf<Int,MutableList<StarterCandidate>?>()
// populate candidate maps
structure.allCandidates().first().map {
when (it) {
is CommandCandidate -> {
// update commandsByTrait
// TODO refactor into generic function to eliminate duplicate code
var commandsByTraitList: MutableList<CommandCandidate>? = commandsByTrait.get(it.trait.factory.traitId: String)
if(commandsByTraitList == null) { commandsByTraitList = arrayListOf<CommandCandidate>() }
commandsByTraitList.add(it)
commandsByTrait.put(it.trait.factory.traitId, commandsByTraitList)
// update commandsByDeviceType
// TODO refactor into generic function to eliminate duplicate code
for (t in it.types) {
//TODO filter out device types not present in the home
//TODO how to get a reference to device type id?
var commandsByDeviceTypeList: MutableList<CommandCandidate>? = commandsByDeviceType.get(t.factory.typeId: Id)
if (commandsByDeviceTypeList == null) { commandsByDeviceTypeList = arrayListOf<CommandCandidate>() }
commandsByDeviceTypeList.add(it)
commandsByDeviceType.put(t.factory.typeId, commandsByDeviceTypeList)
}
// update commandsByEntity
// TODO refactor into generic function to eliminate duplicate code
var commandsByEntityList: MutableList<CommandCandidate>? = commandsByEntity.get(it.entity.id: Id)
if ( commandsByEntityList == null ) {commandsByEntityList = arrayListOf<CommandCandidate>()}
commandsByEntityList.add(it)
commandsByEntity.put(it.entity.id, commandsByEntityList)
}
/*
is StateReaderCandidate -> {
// update stateReadersByTrait
var stateReadersList: MutableList<StateReaderCandidate>? = stateReadersByTrait.get(it.trait.factory.traitId)
if(stateReadersList == null) { stateReadersList = arrayListOf<StateReaderCandidate>() }
stateReadersList.add(it)
stateReadersByTrait.put(it.trait.factory.traitId, stateReadersList)
// update stateReadersByDeviceType
for (t in it.types) {
//TODO filter out device types not present in the home
var stateReadersList: MutableList<StateReaderCandidate>? = stateReadersByDeviceType.get(t.metadata.hashcode())
if (stateReadersList == null) { stateReadersList = arrayListOf<StateReaderCandidate>() }
stateReadersList.put(it)
stateReadersByDeviceType.put(t.metadata.hashCode(),deviceTypeStateReaderList)
}
// update stateReadersByEntity
MutableList<StateReaderCandidate> entityStateReaderList? = stateReadersByEntity.get(it.entity.id)
if entityStateReaderList is null {entityStateReaderList = arrayListOf<StateReaderCandidate>()}
entityStateReaderList.add(it)
stateReadersByEntity.put(it.entity.id, entityStateReaderList)
}
*/
/*
is StarterCandidate -> {
// update startersByTrait
var startersList: MutableList<StateReaderCandidate>? = startersByTrait.get(it.trait.factory.traitId)
if(startersList == null) { startersList = arrayListOf<StateReaderCandidate>() }
startersList.add(it)
startersByTrait.put(it.trait.factory.traitId, startersList)
// update startersByDeviceType
for (t in it.types) {
//TODO filter out device types not present in the home
var startersList: MutableList<StateReaderCandidate>? = startersByDeviceType.get(t.metadata.hashcode())
if (startersList == null) { startersList = arrayListOf<StateReaderCandidate>() }
startersList.put(it)
startersByDeviceType.put(t.metadata.hashCode(),deviceTypeStateReaderList)
}
// update startersByEntity
MutableList<StateReaderCandidate> entityStateReaderList? = startersByEntity.get(it.entity.id)
if entityStateReaderList is null {entityStateReaderList = arrayListOf<StateReaderCandidate>()}
entityStateReaderList.add(it)
stateReadersByEntity.put(it.entity.id, entityStateReaderList)
}
*/
else -> println("unknown type encountered: " + it)
}
}
เรียกใช้การทำงานอัตโนมัติ
เรียกใช้การทำงานอัตโนมัติที่สร้างขึ้นโดยใช้วิธี 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()
รับการทำงานอัตโนมัติตามรหัส
หากต้องการดูการทำงานอัตโนมัติตามรหัสการทำงานอัตโนมัติ ให้เรียกใช้เมธอด 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()
ของการทำงานนั้น โดยส่งนิพจน์ 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()
รองรับการเปลี่ยนกราฟการทำงานอัตโนมัติโดยสมบูรณ์ แต่จะไม่รองรับการแก้ไขกราฟต่อโหนด การแก้ไขต่อโหนดมีแนวโน้มที่จะเกิดข้อผิดพลาดเนื่องจากโหนดมีความเกี่ยวข้องกัน หากต้องการเปลี่ยนตรรกะของการทํางานอัตโนมัติ ให้สร้างกราฟใหม่และแทนที่กราฟที่มีอยู่
ลบการทำงานอัตโนมัติ
หากต้องการลบการทำงานอัตโนมัติ ให้ใช้วิธีของโครงสร้าง 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
เงื่อนไขเริ่มต้นนั้นจะเปิดใช้งานการทำงานอัตโนมัติไม่ได้อีกต่อไป