È possibile accedere alle API Automation tramite le API Home per Android, ma poiché il punto di accesso è tramite una struttura, è necessario concedere prima l'autorizzazione alla struttura prima di poterle utilizzare.
Una volta concesse le autorizzazioni per una struttura, importa questi pacchetti nella tua app:
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.Id
import com.google.home.Structure
Una struttura contiene un'
HasAutomations
interfaccia con i seguenti metodi specifici per l'automazione:
| API | Descrizione |
|---|---|
automations() |
Elenca tutte le automazioni che appartengono alla struttura. Vengono restituite solo le automazioni che hai creato tramite le API Home. |
createAutomation(automation) |
Crea un'istanza di automazione per una struttura. |
deleteAutomation(automationId) |
Elimina un'istanza di automazione in base al suo ID. |
Crea un'automazione
Dopo aver creato un'istanza di Home e aver ricevuto le autorizzazioni dall'utente, recupera la struttura e i dispositivi:
val structure = homeManager.structures().list().single()
val device = homeManager.devices().get(Id("myDevice"))!!
Quindi, definisci la logica dell'automazione utilizzando Automation DSL. Nelle API Home, un'automazione è rappresentata dall'interfaccia Automation. Questa interfaccia contiene un insieme di proprietà:
- Metadati, come nome e descrizione.
- Flag che indicano, ad esempio, se l'automazione può essere eseguita o meno.
- Un elenco di nodi che contengono la logica dell'automazione, chiamato grafico di automazione, rappresentato dalla proprietà
automationGraph.
Per impostazione predefinita, automationGraph è di tipo SequentialFlow, ovvero una classe che contiene un elenco di nodi che vengono eseguiti in ordine sequenziale. Ogni nodo rappresenta un elemento dell'automazione, come un comando iniziale, una condizione o un'azione.
Assegna all'automazione un name e una description.
Per impostazione predefinita, la creazione di un'automazione imposta il flag isActive su true, pertanto non è necessario impostarlo in modo esplicito, a meno che non vuoi che l'automazione sia inizialmente disattivata. In questo scenario, imposta il flag su false durante la creazione.
L'interfaccia DraftAutomation viene utilizzata per creare e creare automazioni,
mentre l'interfaccia Automation viene utilizzata per il recupero. Ad esempio, ecco l'automazione DSL per un'automazione che attiva un dispositivo quando ne viene attivato un altro:
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()) }
}
}
Una volta definita l'automazione DSL, passala al
createAutomation()
metodo per creare l'istanza DraftAutomation:
val createdAutomation = structure.createAutomation(automation)
Da qui, puoi utilizzare tutti gli altri metodi di automazione sull'automazione, come execute(), stop() e update().
Errori di convalida
Se la creazione dell'automazione non supera la convalida, un messaggio di avviso o di errore fornisce informazioni sul problema. Per ulteriori informazioni, consulta il
ValidationIssueType riferimento.
Esempi di codice
Di seguito sono riportati alcuni esempi di codice che potrebbero essere utilizzati per implementare parti delle automazioni ipotetiche descritte nella pagina Progettare un'automazione su Android.
Automazione semplice
Un'automazione che alza le tende alle 8:00 potrebbe essere implementata in questo modo:
// 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.
}
Automazione complessa
Un'automazione che attiva le luci lampeggianti quando viene rilevato un movimento potrebbe essere implementata in questo modo:
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())}
}
}
}
}
Seleziona dinamicamente i dispositivi con i filtri di entità
Quando scrivi un'automazione, non sei limitato a specificare dispositivi specifici. Una funzionalità chiamata filtri di entità consente all'automazione di selezionare i dispositivi in fase di runtime in base a vari criteri.
Ad esempio, utilizzando i filtri di entità, l'automazione potrebbe essere destinata a:
- Tutti i dispositivi di un determinato tipo di dispositivo
- Tutti i dispositivi in una stanza specifica
- Tutti i dispositivi di un determinato tipo di dispositivo in una stanza specifica
- Tutti i dispositivi accesi
- Tutti i dispositivi accesi in una stanza specifica
Per utilizzare i filtri di entità:
- Chiama
atExecutionTime()suStructureoRoom. Viene restituito unTypedExpression<TypedEntity<StructureType>>. - Su questo oggetto, chiama
getDevicesOfType(), passandogli unDeviceType.
I filtri di entità possono essere utilizzati in comandi iniziali, lettori di stato e azioni.
Ad esempio, per fare in modo che qualsiasi luce On/Off attivi un'automazione da un comando iniziale:
// If any light is turned on or off val starter = starter( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, )
Per acquisire lo stato OnOff di tutte le luci in una struttura (in particolare, le luci On/Off) in un lettore di stato:
// Build a Map<Entity, OnOff> val onOffStateOfAllLights = stateReader( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, )
Per ottenere le luci in una stanza specifica e utilizzarle in una condizione:
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 } }
In fase di runtime:
| Scenario | Risultato |
|---|---|
| Nessun dispositivo soddisfa i criteri in un comando iniziale. | L'automazione non viene attivata. |
| Nessun dispositivo soddisfa i criteri in un lettore di stato. | L'automazione viene avviata, ma continuerà in base al nodo della condizione. |
| Nessun dispositivo soddisfa i criteri in un'azione. | L'automazione viene avviata, ma l'azione non fa nulla. |
L'esempio seguente è un'automazione che spegne tutte le luci tranne la luce del corridoio ogni volta che viene spenta una singola luce:
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()) } } }
Esegui un'automazione
Esegui un'automazione creata utilizzando il
execute()
metodo:
createdAutomation.execute()
Se l'automazione ha un
comando iniziale manuale, execute()
avvia l'automazione da quel punto, ignorando tutti i nodi che precedono il
comando iniziale manuale. Se l'automazione non ha un comando iniziale manuale, l'esecuzione inizia dal nodo che segue il primo nodo del comando iniziale.
Se l'operazione execute() non riesce, potrebbe essere generata una HomeException. Consulta Gestione degli errori.
Arresta un'automazione
Arresta un'automazione in esecuzione utilizzando il metodo stop():
createdAutomation.stop()
Se l'operazione stop() non riesce, potrebbe essere generata una HomeException. Consulta Gestione degli errori.
Ottieni un elenco di automazioni per una struttura
Le automazioni sono definite a livello di struttura. Raccogli le `automations()` della struttura
automations()
per accedere a un Flow di automazioni:
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"))
}
}
In alternativa, assegnalo a una Collection locale:
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()
Ottieni un'automazione in base all'ID
Per ottenere un'automazione in base all'ID dell'automazione, chiama il
automations()
metodo sulla struttura e cerca la corrispondenza in base all'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()
Risposta:
// 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")
]))
Ottieni un'automazione in base al nome
Il
filter()
metodo in Kotlin può essere utilizzato per perfezionare ulteriormente le chiamate API. Per ottenere un'automazione in base al nome, recupera le automazioni della struttura e filtra in base al nome dell'automazione:
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") }
Ottieni tutte le automazioni per un dispositivo
Per ottenere tutte le automazioni che fanno riferimento a un determinato dispositivo, utilizza il filtro nidificato per eseguire la scansione di ogni automationGraph dell'automazione:
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
}
}
}
Aggiorna un'automazione
Per aggiornare i metadati di un'automazione, chiama il suo
update()
metodo, passandogli un'espressione lambda che imposta i metadati:
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" }
Il update()
metodo supporta la sostituzione completa di un grafico di automazione, ma non la modifica per nodo di
il grafico. La modifica per nodo è soggetta a errori a causa delle interdipendenze dei nodi. Se vuoi modificare la logica di un'automazione, genera un nuovo grafico e sostituisci completamente quello esistente.
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")) }
}
}
}
Elimina un'automazione
Per eliminare un'automazione, utilizza il metodo
deleteAutomation()
della struttura. Un'automazione deve essere eliminata utilizzando il suo 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().first()
structure.deleteAutomation(automation.id)
Se l'eliminazione non riesce, potrebbe essere generata una HomeException. Consulta Gestione degli errori.
Impatto dell'eliminazione del dispositivo sulle automazioni
Se un utente elimina un dispositivo utilizzato in un'automazione, il dispositivo eliminato non può attivare alcun comando iniziale e l'automazione non sarà in grado di leggere gli attributi o inviare comandi. Ad esempio, se un utente elimina un
OccupancySensorDevice
dalla propria casa e un'automazione ha un comando iniziale che dipende da
il OccupancySensorDevice, il comando iniziale non può più attivare l'automazione.