Dostęp do interfejsów Automation API można uzyskać za pomocą interfejsów Home API na Androida, ale ponieważ ich punkt wejścia znajduje się w domu, przed ich użyciem należy najpierw przyznać uprawnienia do domu.
Po przyznaniu uprawnień do struktury zaimportuj te pakiety do aplikacji:
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.Id
import com.google.home.Structure
Struktura zawiera interfejs HasAutomations z tymi metodami związanymi z automatyzacją:
| Interfejs API | Opis |
|---|---|
automations() |
Wyświetl listę wszystkich automatyzacji należących do domu. Zwracane są tylko automatyzacje utworzone za pomocą interfejsów Home API. |
createAutomation(automation) |
Utwórz instancję automatyzacji dla domu. |
deleteAutomation(automationId) |
Usuwanie instancji automatyzacji według jej identyfikatora. |
Utwórz automatyzację
Po utworzeniu instancji Home i otrzymaniu uprawnień od użytkownika pobierz dom i urządzenia:
val structure = homeManager.structures().list().single()
val device = homeManager.devices().get(Id("myDevice"))!!
Następnie zdefiniuj logikę automatyzacji za pomocą języka DSL automatyzacji. W interfejsach Home API automatyzacja jest reprezentowana przez interfejs Automation. Ten interfejs zawiera zestaw właściwości:
- metadane, takie jak nazwa i opis;
- Flagi wskazujące na przykład, czy automatyzację można wykonać.
- Lista węzłów zawierających logikę automatyzacji, nazywana wykresem automatyzacji, reprezentowana przez właściwość
automationGraph.
automationGraph domyślnie jest typu SequentialFlow, czyli klasy zawierającej listę węzłów, które są wykonywane w kolejności sekwencyjnej. Każdy węzeł reprezentuje element automatyzacji, taki jak polecenie inicjujące, warunek lub działanie.
Przypisz do automatyzacji name i description.
Podczas tworzenia automatyzacji flaga isActive jest domyślnie ustawiona na true, więc nie trzeba jej ustawiać bezpośrednio, chyba że chcesz, aby automatyzacja była początkowo wyłączona. W takim przypadku podczas tworzenia ustaw flagę na false.
Interfejs DraftAutomation służy do tworzenia automatyzacji, a interfejs Automation do pobierania. Oto przykład języka DSL automatyzacji, która włącza urządzenie, gdy inne urządzenie jest włączone:
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()) }
}
}
Po zdefiniowaniu języka DSL automatyzacji przekaż go do metody createAutomation(), aby utworzyć instancję DraftAutomation:
val createdAutomation = structure.createAutomation(automation)
Możesz tu używać wszystkich innych metod automatyzacji, takich jak execute(), stop() i update().
Błędy weryfikacji
Jeśli utworzenie automatyzacji nie przejdzie weryfikacji, pojawi się komunikat z ostrzeżeniem lub komunikat o błędzie zawierający informacje o problemie. Więcej informacji znajdziesz w dokumentacji ValidationIssueType.
Nawet jeśli metoda createAutomation() zakończy się bez zgłaszania wyjątku, utworzona automatyzacja może być nieprawidłowa lub niemożliwa do wykonania. Backend umożliwia zapisywanie nieprawidłowych wersji roboczych automatyzacji (np. jeśli użytkownik nie wyraził wymaganych zgód, takich jak wykrywanie znajomych twarzy, lub jeśli brakuje funkcji urządzenia).
Zawsze sprawdzaj isValid i validationIssues w zwróconej instancji Automation:
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 -> {}
}
}
}
Przykłady kodu
Poniżej przedstawiamy przykładowy kod, który można wykorzystać do wdrożenia części hipotetycznych automatyzacji opisanych na stronie Projektowanie automatyzacji na Androidzie.
Prosta automatyzacja
Automatyzacja, która podnosi rolety o 8:00, może być zaimplementowana w ten sposób:
// 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.
}
Złożona automatyzacja
Automatyzacja, która powoduje miganie świateł po wykryciu ruchu, może być zaimplementowana w ten sposób:
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())}
}
}
}
}
Dynamiczne wybieranie urządzeń za pomocą filtrów elementów
Podczas tworzenia automatyzacji nie musisz ograniczać się do określania konkretnych urządzeń. Funkcja o nazwie filtry encji umożliwia automatyzacji wybieranie urządzeń w czasie działania na podstawie różnych kryteriów.
Na przykład dzięki filtrom elementów automatyzacja może kierować reklamy na:
- wszystkie urządzenia określonego typu,
- wszystkie urządzenia w konkretnym pomieszczeniu,
- wszystkie urządzenia określonego typu w danym pomieszczeniu,
- wszystkie urządzenia, które są włączone;
- wszystkie urządzenia, które są włączone w konkretnym pomieszczeniu;
Aby użyć filtrów podmiotów:
- Zadzwoń pod numer
atExecutionTime(), korzystając zStructurelubRoom. ZwracaTypedExpression<TypedEntity<StructureType>>. - Wywołaj na tym obiekcie metodę
getDevicesOfType(), przekazując jej obiektDeviceType.
Filtry elementów mogą być używane w uruchomieniach, czytnikach stanu i działaniach.
Aby na przykład dowolne światło włączane/wyłączane uruchamiało automatyzację za pomocą polecenia inicjującego:
// If any light is turned on or off val starter = starter( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, )
Aby zarejestrować stan OnOff wszystkich świateł w domu (konkretnie świateł włączonych/wyłączonych) w czytniku stanu:
// Build a Map<Entity, OnOff> val onOffStateOfAllLights = stateReader( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, )
Aby uzyskać dostęp do oświetlenia w konkretnym pomieszczeniu i użyć go w warunku:
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 } }
W czasie działania:
| Scenariusz | Wynik |
|---|---|
| Żadne urządzenia nie spełniają kryteriów w starterze. | Automatyzacja nie jest aktywowana. |
| Żadne urządzenia nie spełniają kryteriów w czytniku stanu. | Automatyzacja uruchomi się, ale będzie kontynuowana na podstawie węzła warunku. |
| Żadne urządzenia nie spełniają kryteriów działania. | Automatyzacja się uruchamia, ale działanie nic nie robi. |
Poniższy przykład to automatyzacja, która wyłącza wszystkie światła oprócz światła w przedpokoju, gdy wyłączone zostanie pojedyncze światło:
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()) } } }
Wykonywanie automatyzacji
Uruchom utworzoną automatyzację za pomocą metody execute():
createdAutomation.execute()
Jeśli automatyzacja ma element uruchamiany ręcznie, execute() uruchamia automatyzację od tego momentu, ignorując wszystkie węzły, które poprzedzają element uruchamiany ręcznie. Jeśli automatyzacja nie ma ręcznego elementu inicjującego, wykonywanie rozpoczyna się od węzła następującego po pierwszym węźle inicjującym.
Jeśli operacja execute() się nie powiedzie, może zostać zgłoszony błąd HomeException. Zobacz Obsługa błędów.
Zatrzymywanie automatyzacji
Zatrzymaj działającą automatyzację za pomocą metody stop():
createdAutomation.stop()
Jeśli operacja stop() się nie powiedzie, może zostać zgłoszony błąd HomeException. Zobacz Obsługa błędów.
Pobieranie listy automatyzacji w domu
Automatyzacje są definiowane na poziomie domu. Kliknij automations() domu, aby uzyskać dostęp do Flow automatyzacji:
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"))
}
}
Możesz też przypisać go do lokalnego elementu 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()
Pobieranie automatyzacji według identyfikatora
Aby pobrać automatyzację według identyfikatora, wywołaj metodę automations() na domu i dopasuj identyfikator:
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()
Odpowiedź:
// 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")
]))
Pobieranie automatyzacji według nazwy
Do dalszego doprecyzowywania wywołań interfejsu API można użyć metody filter() w języku Kotlin. Aby pobrać automatyzację według nazwy, pobierz automatyzacje domu i przefiltruj je według nazwy automatyzacji:
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") }
Pobieranie wszystkich automatyzacji dla urządzenia
Aby uzyskać wszystkie automatyzacje, które odwołują się do danego urządzenia, użyj zagnieżdżonego filtrowania, aby przeskanować automationGraph każdej automatyzacji:
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
}
}
}
Aktualizowanie automatyzacji
Aby zaktualizować metadane automatyzacji, wywołaj jej metodę update(), przekazując jej wyrażenie lambda, które ustawia metadane:
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" }
Metoda update() obsługuje pełne zastępowanie grafu automatyzacji, ale nie edytowanie poszczególnych węzłów grafu. Edytowanie poszczególnych węzłów jest podatne na błędy ze względu na współzależności między nimi. Jeśli chcesz zmienić logikę automatyzacji, wygeneruj nowy wykres i całkowicie zastąp nim istniejący.
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")) }
}
}
}
Usuwanie automatyzacji
Aby usunąć automatyzację, użyj metody deleteAutomation() obiektu. Automatyzację należy usunąć, używając jej identyfikatora.
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)
Jeśli usunięcie się nie powiedzie, może zostać zgłoszony błąd HomeException. Zobacz Obsługa błędów.
Wpływ usunięcia urządzenia na automatyzacje
Jeśli użytkownik usunie urządzenie używane w automatyzacji, nie będzie ono mogło aktywować żadnych starterów, a automatyzacja nie będzie mogła odczytywać jego atrybutów ani wydawać mu poleceń. Jeśli na przykład użytkownik usunie OccupancySensorDevice ze swojego domu, a automatyzacja ma polecenie inicjujące, które zależy od OccupancySensorDevice, to polecenie nie będzie już mogło aktywować automatyzacji.