Dostęp do interfejsów API automatyzacji można uzyskać za pomocą interfejsów Home API na Androida, ale ponieważ punkt wejścia do nich jest strukturą, przed użyciem tych interfejsów trzeba przyznać uprawnienia do struktury.
Gdy przyznasz uprawnienia do struktury, zaimportuj te pakiety do swojej aplikacji:
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.Id
import com.google.home.Structure
Struktura zawiera
HasAutomations
interfejs z tymi metodami dotyczącymi automatyzacji:
| Interfejs API | Opis |
|---|---|
automations() |
Wyświetla listę wszystkich automatyzacji należących do struktury. Zwracane są tylko automatyzacje utworzone przez Ciebie za pomocą interfejsów Home API. |
createAutomation(automation) |
Tworzy instancję automatyzacji dla struktury. |
deleteAutomation(automationId) |
Usuwa instancję automatyzacji według jej identyfikatora. |
Tworzenie automatyzacji
Po utworzeniu instancji Home i otrzymaniu uprawnień od użytkownika pobierz strukturę i urządzenia:
val structure = homeManager.structures().list().single()
val device = homeManager.devices().get(Id("myDevice"))!!
Następnie zdefiniuj logikę automatyzacji za pomocą Automation DSL. 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, które wskazują na przykład, czy automatyzacja może być wykonywana.
- Lista węzłów zawierających logikę automatyzacji, nazywana grafem automatyzacji, reprezentowana przez właściwość
automationGraph.
Domyślnie automationGraph jest typu SequentialFlow, czyli klasy zawierającej listę węzłów, które są wykonywane w kolejności. Każdy węzeł reprezentuje element automatyzacji, taki jak polecenie inicjujące, warunek lub działanie.
Przypisz automatyzacji name i description.
Podczas tworzenia automatyzacji flaga isActive jest domyślnie ustawiona na true, dlatego nie trzeba jej ustawiać, chyba że na początku chcesz wyłączyć automatyzację. W takim przypadku ustaw flagę na false podczas tworzenia.
Interfejs DraftAutomation służy do tworzenia i tworzenia automatyzacji,
a interfejs Automation – do pobierania. Oto na przykład Automation DSL dla 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()) }
}
}
Gdy zdefiniujesz Automation DSL, przekaż ją do
createAutomation()
metody, aby utworzyć instancję DraftAutomation:
val createdAutomation = structure.createAutomation(automation)
Od tego momentu możesz używać wszystkich innych metod automatyzacji, takich jak execute(), stop() i update().
Błędy weryfikacji
Jeśli utworzenie automatyzacji nie przejdzie weryfikacji, pojawi się ostrzeżenie lub komunikat o błędzie z informacjami o problemie. Więcej informacji znajdziesz w dokumentacji
ValidationIssueType reference.
Przykłady kodu
Poniżej znajdziesz przykładowy kod, który można wykorzystać do zaimplementowania części hipotetycznych automatyzacji opisanych na stronie Projektowanie automatyzacji na Androidzie.
Prosta automatyzacja
Automatyzację, która podnosi rolety o 8:00, można zaimplementować 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
Automatyzację, która włącza migające światła po wykryciu ruchu, można zaimplementować 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 encji
Podczas pisania automatyzacji nie musisz określać konkretnych urządzeń. Funkcja filtry encji umożliwia automatyzacji wybieranie urządzeń w czasie wykonywania na podstawie różnych kryteriów.
Na przykład za pomocą filtrów encji automatyzacja może kierować reklamy na:
- wszystkie urządzenia określonego typu,
- wszystkie urządzenia w danym pomieszczeniu,
- wszystkie urządzenia określonego typu w danym pomieszczeniu,
- wszystkie włączone urządzenia,
- wszystkie włączone urządzenia w danym pomieszczeniu.
Aby użyć filtrów encji:
- W przypadku
StructurelubRoomwywołajatExecutionTime(). Spowoduje to zwrócenieTypedExpression<TypedEntity<StructureType>>. - W tym obiekcie wywołaj
getDevicesOfType(), przekazując muDeviceType.
Filtry encji można stosować w poleceniach inicjujących, czytnikach stanu i działaniach.
Aby dowolne światło włączone/wyłączone uruchamiało automatyzację z polecenia inicjującego:
// If any light is turned on or off val starter = starter( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, )
Aby przechwycić stan OnOff wszystkich świateł w strukturze (w szczególności świateł włączonych/wyłączonych) w czytniku stanu:
// Build a Map<Entity, OnOff> val onOffStateOfAllLights = stateReader( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, )
Aby pobrać światła w danym pomieszczeniu i użyć ich 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 wykonywania:
| Scenariusz | Wynik |
|---|---|
| Żadne urządzenia nie spełniają kryteriów w poleceniu inicjującym. | Automatyzacja nie jest uruchamiana. |
| Żadne urządzenia nie spełniają kryteriów w czytniku stanu. | Automatyzacja uruchamia się, ale będzie kontynuowana na podstawie węzła warunku. |
| Żadne urządzenia nie spełniają kryteriów w działaniu. | Automatyzacja uruchamia się, ale działanie nie robi nic. |
Poniższy przykład to automatyzacja, która wyłącza wszystkie światła z wyjątkiem światła w korytarzu, gdy wyłączone zostanie dowolne ś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ą
execute()
metody:
createdAutomation.execute()
Jeśli automatyzacja ma
ręczne polecenie inicjujące, execute()
uruchamia automatyzację od tego momentu, ignorując wszystkie węzły poprzedzające
ręczne polecenie inicjujące. Jeśli automatyzacja nie ma ręcznego polecenia inicjującego, wykonywanie rozpoczyna się od węzła następującego po pierwszym węźle polecenia inicjującego.
Jeśli operacja execute() się nie powiedzie, może zostać zgłoszony wyjątek 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 wyjątek HomeException. Zobacz Obsługa
błędów.
Pobieranie listy automatyzacji dla struktury
Automatyzacje są definiowane na poziomie struktury. Aby uzyskać dostęp do Flow automatyzacji, zbierz dane w strukturze's
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()
structure.automations().collect {
println("Available automations:")
for (automation in it) {
println(String.format("%S %S", "$automation.id", "$automation.name"))
}
}
Możesz też przypisać ją do lokalnej 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
automations()
metodę w strukturze i dopasuj ją do 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().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
Metodę
filter()
w Kotlinie można użyć do dalszego doprecyzowania wywołań interfejsu API. Aby pobrać automatyzację według nazwy, pobierz automatyzacje struktury i odfiltruj 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 pobrać wszystkie automatyzacje, które odwołują się do danego urządzenia, użyj filtrowania zagnieżdżonego, 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
update()
metodę, 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 grafu na poziomie węzła. Edytowanie na poziomie węzła jest podatne na błędy ze względu na wzajemne zależności węzłów. Jeśli chcesz zmienić logikę automatyzacji, wygeneruj nowy graf i całkowicie zastąp 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 struktury
deleteAutomation(). Automatyzację trzeba usunąć za pomocą 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 wyjątek 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, usunięte urządzenie nie będzie mogło uruchamiać żadnych poleceń inicjujących, a automatyzacja nie będzie mogła odczytywać z niego atrybutów ani wysyłać do niego poleceń. Jeśli na przykład użytkownik usunie
OccupancySensorDevice
z domu, a automatyzacja ma polecenie inicjujące, które zależy od
OccupancySensorDevice, to polecenie inicjujące nie będzie już mogło aktywować automatyzacji.