Automation API dapat diakses melalui Home API, tetapi karena titik entrinya adalah melalui struktur, izin harus diberikan terlebih dahulu pada struktur sebelum dapat digunakan.
Setelah izin diberikan untuk struktur, impor paket ini ke aplikasi Anda:
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.Id
import com.google.home.Structure
Struktur berisi antarmuka
HasAutomations
dengan metode khusus otomatisasi berikut:
API | Deskripsi |
---|---|
automations() |
Mencantumkan semua otomatisasi yang termasuk dalam struktur. Hanya otomatisasi yang telah Anda buat melalui Home API yang ditampilkan. |
createAutomation(automation) |
Membuat instance otomatisasi untuk struktur. |
deleteAutomation(automationId) |
Menghapus instance otomatisasi menurut ID-nya. |
Membuat otomatisasi
Setelah membuat instance Home dan menerima izin dari pengguna, dapatkan struktur dan perangkat:
val structure = homeManager.structures().list().single()
val device = homeManager.devices().get(Id("myDevice"))!!
Kemudian, tentukan logika otomatisasi Anda menggunakan Automation DSL. Di Home API,
otomatisasi direpresentasikan oleh antarmuka Automation
. Antarmuka ini
berisi kumpulan properti:
- Metadata, seperti nama dan deskripsi.
- Flag yang menunjukkan, misalnya, apakah otomatisasi dapat dijalankan atau tidak.
- Daftar node yang berisi logika otomatisasi, yang disebut
grafik otomatisasi, yang direpresentasikan oleh properti
automationGraph
.
automationGraph
, secara default, adalah jenis SequentialFlow
, yang merupakan class
yang berisi daftar node yang dieksekusi dalam urutan berurutan. Setiap node
mewakili elemen otomatisasi, seperti pemicu, kondisi, atau
tindakan.
Tetapkan name
dan description
ke otomatisasi.
Pembuatan otomatisasi menetapkan flag isActive
secara default ke true
, sehingga Anda tidak perlu menetapkan flag ini secara eksplisit, kecuali jika Anda awalnya ingin otomatisasi dinonaktifkan. Dalam skenario tersebut, tetapkan tanda ke false
selama
pembuatan.
Antarmuka DraftAutomation
digunakan untuk mem-build dan membuat otomatisasi,
dan antarmuka Automation
digunakan untuk pengambilan. Misalnya, berikut adalah
Automation DSL untuk otomatisasi yang mengaktifkan perangkat saat perangkat lain
diaktifkan:
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()) }
}
}
Setelah DSL otomatisasi ditentukan, teruskan ke metode createAutomation()
untuk membuat instance DraftAutomation
:
val createdAutomation = structure.createAutomation(automation)
Dari sini, Anda dapat menggunakan semua metode otomatisasi lainnya pada otomatisasi, seperti
execute()
, stop()
, dan update()
.
Error validasi
Jika pembuatan otomatisasi tidak lulus validasi, pesan peringatan atau error akan memberikan informasi tentang masalah tersebut. Untuk informasi selengkapnya, lihat
referensi ValidationIssueType
.
Contoh kode
Di sini kami menyajikan beberapa contoh kode yang dapat digunakan untuk menerapkan bagian dari otomatisasi hipotetis yang dijelaskan di halaman Mendesain otomatisasi.
Otomatisasi sederhana
Otomatisasi yang mengangkat tirai pada pukul 08.00 dapat diterapkan seperti ini:
// 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.
}
Otomatisasi kompleks
Otomatisasi yang memicu lampu berkedip saat gerakan terdeteksi dapat diterapkan seperti ini:
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())}
}
}
}
}
Otomatisasi generik
Kode berikut menunjukkan cara mengumpulkan data tentang rumah pengguna menggunakan Structure API, Device API, dan Discovery API untuk menerapkan aplikasi yang memandu pengguna membuat otomatisasi generik mereka sendiri (seperti yang dijelaskan dalam Mendesain otomatisasi: Otomatisasi generik).
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)
}
}
Menjalankan otomatisasi
Jalankan otomatisasi yang dibuat menggunakan metode execute()
:
createdAutomation.execute()
Jika otomatisasi memiliki
pemicu manual, execute()
akan memulai otomatisasi dari titik tersebut, dengan mengabaikan semua node yang mendahului
pemicu manual. Jika otomatisasi tidak memiliki pemicu manual, eksekusi dimulai dari node yang mengikuti node pemicu pertama.
Jika operasi execute()
gagal, HomeException
dapat ditampilkan. Lihat Penanganan
error.
Menghentikan otomatisasi
Hentikan otomatisasi yang sedang berjalan menggunakan metode stop()
:
createdAutomation.stop()
Jika operasi stop()
gagal, HomeException
dapat ditampilkan. Lihat Penanganan
error.
Mendapatkan daftar otomatisasi untuk struktur
Otomatisasi ditentukan di tingkat struktur. Kumpulkan di automations()
struktur untuk mengakses Flow
otomatisasi:
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"))
}
}
Atau, tetapkan ke Collection
lokal:
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()
Mendapatkan otomatisasi menurut ID
Untuk mendapatkan otomatisasi berdasarkan ID otomatisasi, panggil metode automations()
pada struktur, dan cocokkan dengan 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()
Respons:
// 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")
]))
Mendapatkan otomatisasi berdasarkan nama
Metode
filter()
di Kotlin dapat digunakan untuk lebih meningkatkan kualitas panggilan API. Untuk mendapatkan otomatisasi
berdasarkan nama, dapatkan otomatisasi struktur dan filter berdasarkan nama otomatisasi:
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') }
Mendapatkan semua otomatisasi untuk perangkat
Untuk mendapatkan semua otomatisasi yang mereferensikan perangkat tertentu, gunakan pemfilteran bertingkat
untuk memindai automationGraph
setiap otomatisasi:
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
}
}
}
Memperbarui otomatisasi
Untuk memperbarui metadata otomatisasi, panggil metode
update()
, dengan meneruskan ekspresi lambda yang menetapkan metadata:
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" }
Metode update()
mendukung penggantian grafik otomatisasi sepenuhnya, tetapi tidak mendukung pengeditan per node
grafik. Pengeditan per node rentan terhadap error karena interdependensi node. Jika
Anda ingin mengubah logika otomatisasi, buat grafik baru dan ganti sepenuhnya
grafik yang ada.
Menghapus otomatisasi
Untuk menghapus otomatisasi, gunakan metode
deleteAutomation()
struktur. Otomatisasi harus dihapus menggunakan ID-nya.
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)
Jika penghapusan gagal, HomeException
mungkin ditampilkan. Lihat Penanganan
error.
Dampak penghapusan perangkat pada otomatisasi
Jika pengguna menghapus perangkat yang digunakan dalam otomatisasi, perangkat yang dihapus
tidak dapat memicu pemicu apa pun, dan otomatisasi tidak akan dapat membaca atribut
darinya, atau mengeluarkan perintah ke perangkat tersebut. Misalnya, jika pengguna menghapus
OccupancySensorDevice
dari rumah, dan otomatisasi memiliki pemicu yang bergantung pada
OccupancySensorDevice
, pemicu tersebut tidak dapat lagi mengaktifkan otomatisasi.