Se puede acceder a las APIs de Automation a través de las APIs de Home para Android, pero, como su punto de entrada es a través de una estructura, primero se debe otorgar permiso en la estructura antes de que se puedan usar.
Una vez que se otorgan los permisos para una estructura, importa estos paquetes a tu app:
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.Id
import com.google.home.Structure
Una estructura contiene una interfaz HasAutomations
con los siguientes métodos específicos de la automatización:
API | Descripción |
---|---|
automations() |
Enumera todas las automatizaciones que pertenecen a la estructura. Solo se devuelven las automatizaciones que creaste a través de las APIs de Home. |
createAutomation(automation) |
Crea una instancia de automatización para una estructura. |
deleteAutomation(automationId) |
Borra una instancia de automatización por su ID. |
Crea una automatización
Después de crear una instancia de Home y recibir permisos del usuario, obtén la estructura y los dispositivos:
val structure = homeManager.structures().list().single()
val device = homeManager.devices().get(Id("myDevice"))!!
Luego, define la lógica de tu automatización con el DSL de Automation. En las APIs de Home, una automatización se representa con la interfaz Automation
. Esta interfaz contiene un conjunto de propiedades:
- Metadatos, como el nombre y la descripción
- Son marcas que indican, por ejemplo, si se puede ejecutar la automatización.
- Es una lista de nodos que contienen la lógica de la automatización, llamada gráfico de automatización, representada por la propiedad
automationGraph
.
De forma predeterminada, automationGraph
es del tipo SequentialFlow
, que es una clase que contiene una lista de nodos que se ejecutan en orden secuencial. Cada nodo representa un elemento de la automatización, como un activador, una condición o una acción.
Asigna a la automatización un name
y un description
.
Cuando se crea una automatización, la marca isActive
se establece de forma predeterminada en true
, por lo que no es necesario establecer esta marca de forma explícita, a menos que inicialmente desees que la automatización esté inhabilitada. En ese caso, establece la marca en false
durante la creación.
La interfaz DraftAutomation
se usa para compilar y crear automatizaciones, y la interfaz Automation
se usa para la recuperación. Por ejemplo, aquí se muestra el DSL de automatización para una automatización que enciende un dispositivo cuando se enciende otro:
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 vez que se define el DSL de automatización, pásalo al método createAutomation()
para crear la instancia de DraftAutomation
:
val createdAutomation = structure.createAutomation(automation)
Desde aquí, puedes usar todos los demás métodos de automatización en la automatización, como execute()
, stop()
y update()
.
Errores de validación
Si la creación de la automatización no pasa la validación, un mensaje de advertencia o error proporciona información sobre el problema. Para obtener más información, consulta la referencia de ValidationIssueType
.
Ejemplos de código
Aquí, presentamos un ejemplo de código que se podría usar para implementar partes de las automatizaciones hipotéticas que se describen en la página Diseña una automatización en Android.
Automatización simple
Una automatización que sube las persianas a las 8 a.m. podría implementarse de la siguiente manera:
// 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.
}
Automatización compleja
Una automatización que activa luces parpadeantes cuando se detecta movimiento podría implementarse de la siguiente manera:
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())}
}
}
}
}
Selecciona dispositivos de forma dinámica con filtros de entidades
Cuando escribes una automatización, no estás limitado a especificar dispositivos determinados. Una función llamada filtros de entidades permite que tu automatización seleccione dispositivos en el tiempo de ejecución según varios criterios.
Por ejemplo, con los filtros de entidades, tu automatización podría segmentar lo siguiente:
- Todos los dispositivos de un tipo de dispositivo en particular
- Todos los dispositivos de una habitación en particular
- Todos los dispositivos de un tipo de dispositivo en particular en una habitación específica
- Todos los dispositivos que están encendidos
- Todos los dispositivos que están encendidos en una habitación en particular
Para usar filtros de entidades, sigue estos pasos:
- En
Structure
oRoom
, llama aatExecutionTime()
. Esto devuelve unTypedExpression<TypedEntity<StructureType>>
. - En este objeto, llama a
getDevicesOfType()
y pásale unDeviceType
.
Los filtros de entidades se pueden usar en activadores, lectores de estado y acciones.
Por ejemplo, para que cualquier luz de encendido/apagado active una automatización desde un activador, haz lo siguiente:
// If any light is turned on or off val starter = starter( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, )
Para capturar el estado OnOff
de todas las luces de una estructura (específicamente, las luces de encendido/apagado) en un lector de estado, haz lo siguiente:
// Build a Map<Entity, OnOff> val onOffStateOfAllLights = stateReader( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, )
Para obtener las luces de una habitación en particular y usarlas en una condición, haz lo siguiente:
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 } }
En el tiempo de ejecución:
Situación | Resultado |
---|---|
Ningún dispositivo cumple con los criterios de un activador. | La automatización no se activa. |
Ningún dispositivo cumple con los criterios en un lector de estado. | La automatización se inicia, pero no hace nada. |
Ningún dispositivo cumple con los criterios de una acción. | La automatización se inicia, pero la acción no hace nada. |
En el siguiente ejemplo, se muestra una automatización que apaga todas las luces, excepto la del pasillo, cada vez que se apaga una luz individual:
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()) } } }
Ejecuta una automatización
Ejecuta una automatización creada con el método execute()
:
createdAutomation.execute()
Si la automatización tiene un activador manual, execute()
inicia la automatización desde ese punto y omite todos los nodos que preceden al activador manual. Si la automatización no tiene un activador manual, la ejecución comienza desde el nodo que sigue al primer nodo de activador.
Si falla la operación execute()
, se puede arrojar una HomeException
. Consulta Manejo de errores.
Cómo detener una automatización
Detén una automatización en ejecución con el método stop()
:
createdAutomation.stop()
Si falla la operación stop()
, se puede arrojar una HomeException
. Consulta Manejo de errores.
Obtén una lista de las automatizaciones de una estructura
Las automatizaciones se definen a nivel de la estructura. Recopila información sobre la automations()
de la estructura para acceder a un Flow
de automatizaciones:
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"))
}
}
Como alternativa, asígnale un Collection
local:
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()
Obtén una automatización por ID
Para obtener una automatización por ID, llama al método automations()
en la estructura y haz coincidir el 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()
Respuesta:
// 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")
]))
Obtén una automatización por su nombre
El método filter()
en Kotlin se puede usar para definir mejor las llamadas a la API. Para obtener una automatización por nombre, obtén las automatizaciones de la estructura y filtra por el nombre de la automatización:
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") }
Obtener todas las automatizaciones de un dispositivo
Para obtener todas las automatizaciones que hacen referencia a un dispositivo determinado, usa el filtrado anidado para analizar el campo automationGraph
de cada automatización:
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
}
}
}
Actualiza una automatización
Para actualizar los metadatos de una automatización, llama a su método update()
y pásale una expresión lambda que establezca los metadatos:
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" }
El método update()
admite el reemplazo completo de un grafo de automatización, pero no la edición por nodo del grafo. La edición por nodo es propensa a errores debido a las interdependencias entre los nodos. Si deseas cambiar la lógica de una automatización, genera un gráfico nuevo y reemplaza por completo el existente.
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")) }
}
}
}
Cómo borrar una automatización
Para borrar una automatización, usa el método deleteAutomation()
de la estructura. Una automatización se debe borrar con su 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)
Si falla la eliminación, es posible que se arroje un HomeException
. Consulta Manejo de errores.
Impacto de la eliminación de dispositivos en las automatizaciones
Si un usuario borra un dispositivo que se usa en una automatización, el dispositivo borrado no podrá activar ningún iniciador, y la automatización no podrá leer sus atributos ni enviarle comandos. Por ejemplo, si un usuario borra un OccupancySensorDevice
de su casa y una automatización tiene un activador que depende del OccupancySensorDevice
, ese activador ya no podrá activar la automatización.