È possibile accedere alle API Automation tramite le API Home per Android, ma poiché il loro punto di accesso è una struttura, è necessario concedere l'autorizzazione per la 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'interfaccia
HasAutomations
con i seguenti metodi specifici per l'automazione:
API | Descrizione |
---|---|
automations() |
Elenca tutte le automazioni appartenenti alla casa. 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 relativo 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, ad esempio nome e descrizione.
- Flag che indicano, ad esempio, se l'automazione può essere eseguita.
- Un elenco di nodi che contengono la logica dell'automazione, chiamato
grafico dell'automazione, rappresentato dalla proprietà
automationGraph
.
automationGraph
, per impostazione predefinita, è 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, ad esempio un trigger, una condizione o
un'azione.
Assegna all'automazione un name
e un description
.
La creazione di un'automazione imposta il flag isActive
su true
, pertanto non è necessario impostarlo esplicitamente, a meno che non si voglia disattivare inizialmente l'automazione. In questo scenario, imposta il flag su false
durante
la creazione.
L'interfaccia DraftAutomation
viene utilizzata per creare automazioni,
mentre l'interfaccia Automation
viene utilizzata per il recupero. Ad esempio, ecco l'DSL
di automazione per un'automazione che accende un dispositivo quando un altro dispositivo
viene acceso:
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 la DSL di automazione, passala al metodo
createAutomation()
per creare l'istanza DraftAutomation
:
val createdAutomation = structure.createAutomation(automation)
Da qui, puoi utilizzare tutti gli altri metodi di automazione, ad esempio 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 saperne di più, consulta il
riferimento ValidationIssueType
.
Esempi di codice
Di seguito è riportato un codice di esempio che potrebbe essere utilizzato per implementare parti delle automazioni ipotetiche descritte nella pagina Progettare un'automazione su Android.
Automazione semplice
Un'automazione che alza le tapparelle alle 8:00 potrebbe essere implementata nel seguente 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 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())}
}
}
}
}
Selezionare dinamicamente i dispositivi con i filtri delle entità
Quando scrivi un'automazione, non sei limitato a specificare dispositivi particolari. Una funzionalità chiamata filtri entità consente all'automazione di selezionare i dispositivi in fase di runtime in base a vari criteri.
Ad esempio, utilizzando i filtri delle entità, l'automazione potrebbe scegliere come target:
- tutti i dispositivi di un determinato tipo
- tutti i dispositivi in una stanza specifica
- tutti i dispositivi di un determinato tipo in una stanza specifica
- tutti i dispositivi accesi
- tutti i dispositivi accesi in una determinata stanza
Per utilizzare i filtri per entità:
- Su
Structure
oRoom
, chiama il numeroatExecutionTime()
. Viene restituito unTypedExpression<TypedEntity<StructureType>>
. - Su questo oggetto, chiama
getDevicesOfType()
, passando unDeviceType
.
I filtri per 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 di una struttura (in particolare,
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 di una determinata stanza 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 } }
Al runtime:
Scenario | Risultato |
---|---|
Nessun dispositivo soddisfa i criteri di un comando iniziale. | L'automazione non viene attivata. |
Nessun dispositivo soddisfa i criteri in un lettore di stato. | L'automazione si avvia, ma non fa nulla. |
Nessun dispositivo soddisfa i criteri in un'azione. | L'automazione viene avviata, ma l'azione non fa nulla. |
Il seguente esempio è un'automazione che spegne tutte le luci tranne quella dell'ingresso ogni volta che una singola luce viene spenta:
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()) } } }
Eseguire un'automazione
Esegui un'automazione creata utilizzando il
metodo execute()
:
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 avvio manuale, l'esecuzione
inizia dal nodo successivo al primo nodo di avvio.
Se l'operazione execute()
non riesce, potrebbe essere generato un HomeException
. Consulta la sezione Gestione
degli errori.
Interrompere un'automazione
Interrompi un'automazione in esecuzione utilizzando il metodo stop()
:
createdAutomation.stop()
Se l'operazione stop()
non riesce, potrebbe essere generato un HomeException
. Consulta la sezione Gestione
degli errori.
Visualizzare un elenco di automazioni per una struttura
Le automazioni vengono definite a livello di struttura. Raccogli informazioni sulla
automations()
della struttura 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 un 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 automazione, chiama il metodo
automations()
sulla struttura e trova 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")
]))
Ottenere un'automazione in base al nome
Il metodo
filter()
in Kotlin può essere utilizzato per perfezionare ulteriormente le chiamate API. Per ottenere un'automazione
per 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") }
Recuperare tutte le automazioni per un dispositivo
Per ottenere tutte le automazioni che fanno riferimento a un determinato dispositivo, utilizza il filtro nidificato
per esaminare il automationGraph
di ogni 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 metodo
update()
, 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 metodo update()
supporta la sostituzione completa di un grafico di automazione, ma non la modifica per nodo
del grafico. La modifica per nodo è soggetta a errori a causa delle interdipendenze tra i 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")) }
}
}
}
Eliminare un'automazione
Per eliminare un'automazione, utilizza il metodo
deleteAutomation()
della struttura. Un'automazione deve essere eliminata utilizzando il relativo 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 va a buon fine, potrebbe essere generato un HomeException
. Consulta la sezione Gestione
degli errori.
Impatto dell'eliminazione dei dispositivi sulle automazioni
Se un utente elimina un dispositivo utilizzato in un'automazione, il dispositivo eliminato
non può attivare alcun trigger e l'automazione non sarà in grado di leggere gli attributi
o inviare comandi. Ad esempio, se un utente elimina un
OccupancySensorDevice
dalla sua casa e un'automazione ha un comando iniziale che dipende dal
OccupancySensorDevice
, questo comando iniziale non può più attivare l'automazione.