As APIs de automação podem ser acessadas pelas APIs Home para Android, mas como o ponto de entrada delas é uma estrutura, primeiro é preciso conceder permissão na estrutura antes de usá-las.
Depois que as permissões forem concedidas a uma estrutura, importe estes pacotes para seu app:
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.Id
import com.google.home.Structure
Uma estrutura contém uma interface HasAutomations
com os seguintes métodos específicos de automação:
API | Descrição |
---|---|
automations() |
Liste todas as automações que pertencem à estrutura. Somente as automações criadas por você com as APIs Home são retornadas. |
createAutomation(automation) |
Cria uma instância de automação para uma estrutura. |
deleteAutomation(automationId) |
Exclui uma instância de automação pelo ID. |
Criar uma automação
Depois de criar uma instância do Home e receber permissões do usuário, receba a estrutura e os dispositivos:
val structure = homeManager.structures().list().single()
val device = homeManager.devices().get(Id("myDevice"))!!
Em seguida, defina a lógica da sua automação usando a DSL de automação. Nas APIs Home, uma automação é representada pela interface Automation
. Essa interface contém um conjunto de propriedades:
- Metadados, como nome e descrição.
- Flags que indicam, por exemplo, se a automação pode ser executada ou não.
- Uma lista de nós que contêm a lógica da automação, chamada de
gráfico de automação, representada pela propriedade
automationGraph
.
Por padrão, automationGraph
é do tipo SequentialFlow
, que é uma classe que contém uma lista de nós executados em ordem sequencial. Cada nó representa um elemento da automação, como um gatilho, uma condição ou uma ação.
Atribua um name
e um description
à automação.
A criação de uma automação define a flag isActive
como true
por padrão. Portanto, não é necessário definir explicitamente essa flag, a menos que você queira que a automação seja desativada inicialmente. Nesse cenário, defina a flag como false
durante a criação.
A interface DraftAutomation
é usada para criar automações, e a interface Automation
é usada para recuperação. Por exemplo, confira a DSL de automação para uma automação que liga um dispositivo quando outro é ligado:
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()) }
}
}
Depois que a DSL de automação for definida, transmita-a para o método
createAutomation()
para criar a instância DraftAutomation
:
val createdAutomation = structure.createAutomation(automation)
A partir daqui, você pode usar todos os outros métodos de automação, como execute()
, stop()
e update()
.
Erros de validação
Se a criação da automação não passar na validação, uma mensagem de aviso ou erro
vai fornecer informações sobre o problema. Para mais informações, consulte a
referência de ValidationIssueType
.
Exemplos de código
Aqui, apresentamos um exemplo de código que pode ser usado para implementar partes das automações hipotéticas descritas na página Projetar uma automação no Android.
Automação simples
Uma automação que levanta as persianas às 8h pode ser implementada assim:
// 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.
}
Automação complexa
Uma automação que aciona luzes piscando quando um movimento é detectado pode ser implementada assim:
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())}
}
}
}
}
Selecionar dispositivos dinamicamente com filtros de entidade
Ao escrever uma automação, você não precisa especificar dispositivos específicos. Com os filtros de entidade, a automação pode selecionar dispositivos em tempo de execução com base em vários critérios.
Por exemplo, usando filtros de entidade, sua automação pode segmentar:
- todos os dispositivos de um determinado tipo
- todos os dispositivos em uma sala específica
- todos os dispositivos de um tipo específico em uma sala específica
- todos os dispositivos que estão ligados
- todos os dispositivos ligados em um determinado cômodo
Para usar filtros de entidade:
- Em
Structure
ouRoom
, chameatExecutionTime()
. Isso retorna umTypedExpression<TypedEntity<StructureType>>
. - Nesse objeto, chame
getDevicesOfType()
, transmitindo umDeviceType
.
Os filtros de entidade podem ser usados em ativações, leitores de estado e ações.
Por exemplo, para que qualquer luz on/off acione uma automação de uma ativação:
// If any light is turned on or off val starter = starter( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, )
Para capturar o estado OnOff
de todas as luzes em uma estrutura (especificamente, luzes On/Off) em um leitor de estado:
// Build a Map<Entity, OnOff> val onOffStateOfAllLights = stateReader( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, )
Para pegar as luzes de um cômodo específico e usá-las em uma condição:
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 } }
No ambiente de execução:
Cenário | Resultado |
---|---|
Nenhum dispositivo atende aos critérios em um inicializador. | A automação não é acionada. |
Nenhum dispositivo atende aos critérios em um leitor de estado. | A automação é iniciada, mas não faz nada. |
Nenhum dispositivo atende aos critérios em uma ação. | A automação é iniciada, mas a ação não faz nada. |
O exemplo a seguir é uma automação que apaga todas as luzes, exceto a do corredor, sempre que uma luz individual é apagada:
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()) } } }
Executar uma automação
Execute uma automação criada usando o método
execute()
:
createdAutomation.execute()
Se a automação tiver uma ativação manual, o execute()
vai iniciar a automação a partir desse ponto, ignorando todos os nós que precedem a ativação manual. Se a automação não tiver um iniciador manual, a execução
começará do nó após o primeiro nó iniciador.
Se a operação execute()
falhar, uma HomeException
poderá ser gerada. Consulte Tratamento
de erros.
Parar uma automação
Pare uma automação em execução usando o método stop()
:
createdAutomation.stop()
Se a operação stop()
falhar, uma HomeException
poderá ser gerada. Consulte Tratamento
de erros.
Receber uma lista de automações para uma estrutura
As automações são definidas no nível da estrutura. Colete na automations()
da estrutura
para acessar um Flow
de automações:
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, atribua a um 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()
Receber uma automação por ID
Para receber uma automação por ID, chame o método
automations()
na estrutura e faça a correspondência por 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()
Resposta:
// 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")
]))
Receber uma automação por nome
O método
filter()
em Kotlin pode ser usado para refinar ainda mais as chamadas de API. Para receber uma automação
por nome, extraia as automações da estrutura e filtre pelo nome da automação:
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") }
Receber todas as automações de um dispositivo
Para conferir todas as automações que fazem referência a um determinado dispositivo, use a filtragem aninhada
para verificar o automationGraph
de cada automação:
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
}
}
}
Atualizar uma automação
Para atualizar os metadados de uma automação, chame o método
update()
dela, transmitindo uma expressão lambda que define os metadados:
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" }
O método update()
permite substituir totalmente um gráfico de automação, mas não editar por nó. A edição por nó é propensa a erros devido às interdependências entre eles. Se
você quiser mudar a lógica de uma automação, gere um novo gráfico e substitua
totalmente o atual.
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")) }
}
}
}
Excluir uma automação
Para excluir uma automação, use o método
deleteAutomation()
da estrutura. Uma automação precisa ser excluída usando o ID dela.
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 a exclusão falhar, uma HomeException
poderá ser gerada. Consulte Tratamento
de erros.
Impacto da exclusão de dispositivos nas automações
Se um usuário excluir um dispositivo usado em uma automação, ele não poderá acionar nenhum gatilho, e a automação não poderá ler atributos nem emitir comandos para ele. Por exemplo, se um usuário excluir um
OccupancySensorDevice
da casa dele, e uma automação tiver uma ativação que dependa do
OccupancySensorDevice
, essa ativação não poderá mais iniciar a automação.