As APIs Automation podem ser acessadas pelas APIs Home para Android, mas, como o ponto de entrada é uma estrutura, é preciso primeiro conceder permissão à estrutura para que elas possam ser usadas.
Depois que as permissões forem concedidas para uma estrutura, importe estes pacotes para o 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() |
Lista todas as automações que pertencem à estrutura. Somente as automações criadas por você usando as APIs Home são retornadas. |
createAutomation(automation) |
Criar uma instância de automação para uma estrutura. |
deleteAutomation(automationId) |
Excluir uma instância de automação pelo ID. |
Criar uma automação
Depois de criar uma instância de Home e receber permissões do usuário, acesse 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 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.
- Sinalizações que indicam, por exemplo, se a automação pode ou não ser executada.
- 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.
automationGraph, por padrão, é do tipo SequentialFlow, que é uma classe
que contém uma lista de nós que são executados em ordem sequencial. Cada nó
representa um elemento da automação, como uma ativação, condição ou
ação.
Atribua um name e uma 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 essa flag explicitamente, 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, veja 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 daí, 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 mensagem de erro vai fornecer informações sobre o problema. Para mais informações, consulte a
referência de ValidationIssueType.
Mesmo quando createAutomation() é concluído sem gerar uma exceção, a
automação criada pode não ser válida ou executável. O back-end permite salvar
rascunhos de automação inválidos (por exemplo, se um usuário não tiver concedido os consentimentos
necessários, como a detecção de rosto conhecido, ou se os recursos do dispositivo estiverem ausentes).
Sempre verifique isValid e validationIssues na instância
Automation
retornada:
val createdAutomation = structure.createAutomation(automation)
if (!createdAutomation.isValid) {
// Iterate through validation issues to identify errors and warnings
for (issue in createdAutomation.validationIssues) {
when (issue.severity) {
ValidationIssueSeverity.ERROR -> {
Log.e(
"Automation",
"Validation error on node ${issue.node}: ${issue.issueType}"
)
// Handle error (for example, prompt the user to enable missing
// consents or device features)
}
ValidationIssueSeverity.WARNING -> {
Log.w(
"Automation",
"Validation warning on node ${issue.node}: ${issue.issueType}"
)
}
else -> {}
}
}
}
Exemplos de código
Aqui, apresentamos alguns exemplos de código que podem ser usados para implementar partes das automações hipotéticas descritas na página Criar 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 piscantes 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 de forma dinâmica com filtros de entidade
Ao criar uma automação, você não precisa se limitar a especificar dispositivos específicos. Um recurso chamado filtros de entidade permite que a automação selecione dispositivos no momento da 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 tipo específico
- todos os dispositivos em um ambiente específico
- todos os dispositivos de um tipo específico em um determinado ambiente
- todos os dispositivos que estão ligados
- todos os dispositivos que estão ligados em um ambiente específico
Para usar filtros de entidade:
- Em
StructureouRoom, 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 liga/desliga 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 ligadas/desligadas) em um leitor de estado:
// Build a Map<Entity, OnOff> val onOffStateOfAllLights = stateReader( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, )
Para usar as luzes de um ambiente específico 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 de uma ativação. | A automação não é acionada. |
| Nenhum dispositivo atende aos critérios em um leitor de estado. | A automação é iniciada, mas continua com base no nó de condição. |
| Nenhum dispositivo atende aos critérios de 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, 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 uma ativação manual, a execução
vai começar no nó seguinte ao primeiro nó de ativação.
Se a operação execute() falhar, uma HomeException poderá ser gerada. Consulte Tratamento de erros.
Interromper uma automação
Interrompa 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 no 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-o 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 acessar uma automação pelo ID, chame o método
automations()
na estrutura e faça a correspondência com o 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() no Kotlin pode ser usado para refinar ainda mais as chamadas de API. Para acessar uma automação
pelo nome, acesse 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 receber 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(), 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 a edição por nó
do gráfico. A edição por nó é propensa a erros devido às interdependências entre os nós. 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, o dispositivo excluído
não poderá acionar ativações, e a automação não poderá ler atributos
dele nem emitir comandos para ele. Por exemplo, se um usuário excluir um
OccupancySensorDevice
da casa, e uma automação tiver uma ativação que dependa do
OccupancySensorDevice, essa ativação não poderá mais ativar a automação.