Auf die Automatisierungs-APIs kann über die Home APIs zugegriffen werden. Da ihr Einstiegspunkt jedoch über ein Gebäude erfolgt, muss zuerst die Berechtigung für das Gebäude erteilt werden, bevor sie verwendet werden können.
Nachdem Berechtigungen für ein Gebäude erteilt wurden, importieren Sie die folgenden Pakete in Ihre App:
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.Id
import com.google.home.Structure
Ein Gebäude enthält eine HasAutomations
-Benutzeroberfläche mit den folgenden automatisierungsspezifischen Methoden:
API | Beschreibung |
---|---|
automations() |
Listet alle Automatisierungen auf, die zum Gebäude gehören. Es werden nur automatisierte Abläufe zurückgegeben, die Sie über die Home APIs erstellt haben. |
createAutomation(automation) |
Erstellen Sie eine Automatisierungs-Instanz für ein Gebäude. |
deleteAutomation(automationId) |
Automatisierungsinstanzen anhand ihrer ID löschen |
Automatisierung erstellen
Nachdem Sie eine Instanz von Home erstellt und Berechtigungen vom Nutzer erhalten haben, rufen Sie das Gebäude und die Geräte ab:
val structure = homeManager.structures().list().single()
val device = homeManager.devices().get(Id("myDevice"))!!
Definieren Sie dann die Logik Ihrer Automatisierung mit Automation DSL. In den Home APIs wird eine Automatisierung durch die Automation
-Oberfläche dargestellt. Diese Schnittstelle enthält eine Reihe von Eigenschaften:
- Metadaten wie Name und Beschreibung
- Flags, die beispielsweise angeben, ob die Automatisierung ausgeführt werden kann oder nicht.
- Eine Liste von Knoten, die die Logik der Automatisierung enthalten, der sogenannte Automatisierungsgraph, dargestellt durch die Property
automationGraph
.
automationGraph
hat standardmäßig den Typ SequentialFlow
. Das ist eine Klasse, die eine Liste von Knoten enthält, die in sequenzieller Reihenfolge ausgeführt werden. Jeder Knoten steht für ein Element der Automatisierung, z. B. einen Auslöser, eine Bedingung oder eine Aktion.
Weisen Sie der Automatisierung eine name
und eine description
zu.
Beim Erstellen einer Automatisierung wird das Flag isActive
standardmäßig auf true
gesetzt. Es ist daher nicht erforderlich, dieses Flag explizit festzulegen, es sei denn, Sie möchten die Automatisierung anfangs deaktivieren. Legen Sie in diesem Fall beim Erstellen das Flag auf false
fest.
Die DraftAutomation
-Oberfläche wird zum Erstellen von Automatisierungen verwendet und die Automation
-Oberfläche zum Abrufen. Hier ist beispielsweise die Automation DSL für eine Automatisierung, die ein Gerät einschaltet, wenn ein anderes Gerät eingeschaltet wird:
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()) }
}
}
Nachdem die Automatisierungs-DSL definiert wurde, übergeben Sie sie an die Methode createAutomation()
, um die DraftAutomation
-Instanz zu erstellen:
val createdAutomation = structure.createAutomation(automation)
Hier können Sie alle anderen Automatisierungsmethoden für die Automatisierung verwenden, z. B. execute()
, stop()
und update()
.
Validierungsfehler
Wenn die Erstellung der Automatisierung die Validierung nicht besteht, erhalten Sie eine Warn- oder Fehlermeldung mit Informationen zum Problem. Weitere Informationen finden Sie in der Referenz zu ValidationIssueType
.
Codebeispiele
Hier finden Sie Beispielcode, mit dem Sie Teile der hypothetischen Automatisierungen implementieren können, die auf der Seite Automatisierung entwerfen beschrieben werden.
Einfache Automatisierung
Eine Automatisierung, die die Jalousien um 8:00 Uhr morgens hochfährt, könnte so implementiert werden:
// 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.
}
Komplexe Automatisierung
Eine Automatisierung, die blinkende Lampen auslöst, wenn eine Bewegung erkannt wird, könnte so implementiert werden:
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())}
}
}
}
}
Allgemeine Automatisierung
Im folgenden Code wird gezeigt, wie mithilfe der Structure API, der Device API und der Discovery API Daten zum Zuhause des Nutzers erfasst werden können, um eine App zu implementieren, die den Nutzer durch die Erstellung einer eigenen generischen Automatisierung führt (wie unter Automatisierung entwerfen: Generische Automatisierung beschrieben).
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)
}
}
Automatisierung ausführen
So führen Sie eine erstellte Automatisierung mit der Methode execute()
aus:
createdAutomation.execute()
Wenn die Automatisierung einen manuellen Auslöser hat, startet execute()
die Automatisierung an diesem Punkt und ignoriert alle Knoten, die dem manuellen Auslöser vorausgehen. Wenn die Automatisierung keinen manuellen Auslöser hat, beginnt die Ausführung am Knoten nach dem ersten Auslöserknoten.
Wenn der execute()
-Vorgang fehlschlägt, wird möglicherweise eine HomeException
geworfen. Weitere Informationen finden Sie unter Fehlerbehandlung.
Automatisierung beenden
So beenden Sie eine laufende Automatisierung mit der Methode stop()
:
createdAutomation.stop()
Wenn der stop()
-Vorgang fehlschlägt, wird möglicherweise eine HomeException
geworfen. Weitere Informationen finden Sie unter Fehlerbehandlung.
Liste der Automatisierungen für ein Gebäude abrufen
Automatisierungen werden auf Gebäudeebene definiert. Erfassen Sie die automations()
des Gebäudes, um auf eine Flow
von Automatisierungen zuzugreifen:
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"))
}
}
Alternativ können Sie sie einem lokalen Collection
zuweisen:
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()
Automatisierung nach ID abrufen
Wenn Sie eine Automatisierung anhand der Automatisierungs-ID abrufen möchten, rufen Sie die Methode automations()
für die Struktur auf und gleichen Sie die ID ab:
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()
Response:
// 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")
]))
Automatisierungen anhand des Namens abrufen
Mit der Methode filter()
in Kotlin können API-Aufrufe weiter optimiert werden. Wenn Sie einen automatisierten Ablauf nach Namen abrufen möchten, rufen Sie die automatisierten Abläufe des Gebäudes ab und filtern Sie nach dem Namen des automatisierten Ablaufs:
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') }
Alle automatisierten Abläufe für ein Gerät abrufen
Wenn Sie alle Automatisierungen abrufen möchten, die auf ein bestimmtes Gerät verweisen, verwenden Sie verschachtelte Filter, um die automationGraph
der einzelnen Automatisierungen zu prüfen:
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
}
}
}
Automatisierung aktualisieren
Wenn Sie die Metadaten einer Automatisierung aktualisieren möchten, rufen Sie die Methode update()
auf und übergeben Sie einen Lambda-Ausdruck, mit dem die Metadaten festgelegt werden:
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" }
Mit der Methode update()
kann ein Automatisierungsdiagramm vollständig ersetzt werden, aber nicht Knoten für Knoten bearbeitet werden. Die Bearbeitung pro Knoten ist aufgrund von Knotenabhängigkeiten fehleranfällig. Wenn Sie die Logik einer Automatisierung ändern möchten, erstellen Sie ein neues Diagramm und ersetzen Sie das vorhandene vollständig.
Automatisierung löschen
Wenn Sie eine Automatisierung löschen möchten, verwenden Sie die Methode deleteAutomation()
des Gebäudes. Eine Automatisierung muss anhand ihrer ID gelöscht werden.
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)
Wenn das Löschen fehlschlägt, wird möglicherweise eine HomeException
ausgelöst. Weitere Informationen finden Sie unter Fehlerbehandlung.
Auswirkungen des Löschens von Geräten auf Automatisierungen
Wenn ein Nutzer ein Gerät löscht, das in einer Automatisierung verwendet wird, kann das gelöschte Gerät keine Auslöser mehr aktivieren und die Automatisierung kann keine Attribute daraus lesen oder ihm Befehle senden. Wenn ein Nutzer beispielsweise einen OccupancySensorDevice
aus seinem Zuhause löscht und eine Automatisierung einen Auslöser hat, der vom OccupancySensorDevice
abhängt, kann dieser Auslöser die Automatisierung nicht mehr aktivieren.