Esta é uma visão geral dos conceitos fundamentais da DSL de automação no Android.
Componentes de automação
Uma automação consiste nos seguintes componentes básicos, normalmente avaliados nesta ordem:
- Ativação: define as condições iniciais que ativam a automação, como uma mudança em um atributo. Uma automação precisa ter uma ativação.
- Condição: qualquer outra restrição a ser avaliada depois que uma automação
for ativada. A expressão em uma condição precisa ser avaliada como
truepara que as ações de uma automação sejam executadas. - Ação: comandos ou atualizações de estado que são executados quando todas as condições são atendidas.
Por exemplo, talvez você tenha uma automação que diminui as luzes de um cômodo quando a TV é ligada entre o pôr do sol e o nascer do sol. Neste exemplo:
- Ativador: a TV foi ligada, o que é uma mudança de estado em um atributo de TV.
- Condição: o horário atual da casa em que a TV está é avaliado.
- Ação: as luzes no mesmo ambiente que a TV são reduzidas.
A automação seria ativada quando a TV na sala fosse ligada, mas só seria executada se a condição "o horário está entre o pôr do sol e o nascer do sol" fosse atendida.
Além da estrutura básica, as automações nas APIs Home também contêm metadados, como name e description, que podem ser usados para identificá-las para desenvolvedores e usuários.
Nós
Nas APIs Home, a estrutura lógica de uma automação consiste em nós. Os nós são unidades abstratas e reutilizáveis que representam comportamentos de entidade ou fluxos de execução. Cada nó pode ter variáveis de entrada e de saída que podem ser consumidas por outros nós.
| Nó | Tipo de nó | Implementação em Kotlin | Descrição |
|---|---|---|---|
| Starter | Comportamental |
StarterNodeDsl
|
Inicia uma automação quando o estado de um atributo (qualquer atributo) muda. |
| StateReader | Comportamental |
StateReaderNodeDsl
|
Lê um atributo de trait e permite capturar o valor dele para uso em nós de condição. |
| Ação | Comportamental |
ActionNodeDsl
|
Invoca comandos de trait. |
| Sequencial | Fluxo de execução |
SequentialFlow
|
Executa nós de ação aninhados em sequência. Esse é o comportamento de execução padrão. |
| Paralela | Fluxo de execução |
ParallelFlow
|
Executa nós de ação aninhados em paralelo. |
| Condição | Fluxo de execução |
ConditionNodeDsl
|
Mudar condicionalmente o fluxo de execução com base em avaliações de expressões lógicas. As condições podem ser associadas a uma ativação (condições específicas da ativação) ou ser globais (aplicáveis a todas as ativações). |
| Selecionar | Fluxo de execução |
SelectFlow
|
Permite que mais de uma ativação acione uma automação. |
| IfFlow | Fluxo de execução |
IfFlowDsl
|
Executa no máximo uma ação (ou ramificação) com base em qual condição (se houver) é avaliada como verdadeira (semelhante a uma instrução if-then-else). |
| Expressão | Valor |
Expression
|
Pode ser o valor do atributo de uma característica, uma constante ou um valor literal, e precisa ser avaliado como uma lista, um número, um booleano ou uma string. |
Nós comportamentais
Nós como ativações e ações são nós comportamentais. As ativações iniciam uma automação com base em mudanças nos atributos do dispositivo. As ações emitem comandos de dispositivo ou atualizam atributos.
Os nós comportamentais normalmente estão vinculados a atributos do dispositivo e geram o estado do atributo para uso como entrada em outros nós.
Nós de fluxo de execução
Alguns nós representam fluxos de execução, como sequenciais e paralelos. Cada um desses nós contém os nós comportamentais que definem a automação.
Por exemplo, um fluxo sequencial pode conter nós que são executados em ordem sequencial. Normalmente, elas são ativação, condição e ação.
Uma automação paralela pode ter vários nós de ação em execução ao mesmo tempo, como acender várias luzes simultaneamente. Os nós que seguem um fluxo paralelo não serão executados até que todas as ramificações do fluxo paralelo sejam concluídas.
Outro tipo de fluxo de execução é um fluxo de condição, que pode mudar o fluxo de execução com base na avaliação de uma expressão.
Por exemplo, talvez você tenha uma automação que realiza uma ação com base no período do dia. Um nó de condição verifica a hora do dia e segue o caminho de execução apropriado com base nessa avaliação.
Um fluxo de seleção é útil quando você quer ter mais de uma ativação para a automação. Quando você inclui duas ou mais ativações
em um fluxo select, qualquer uma delas pode ativar a automação.
Por exemplo, você pode criar uma automação que abaixa as persianas ao pôr do sol, se
a temperatura subir acima de um determinado limite ou se o brilho exceder um
limite. Três inicializadores separados lidam com cada um desses cenários, e todos os três
seriam agrupados em um fluxo select.
Um fluxo if-then-else (IfFlow) permite a execução condicional de ramificação
em uma automação. Ao contrário de um nó condition, que interrompe toda a
automação se a condição for false, um fluxo if-then-else executa
ações específicas de forma condicional quando uma condição é verdadeira e pode recorrer a
condições alternativas ou ações de fallback, ou continuar para os nós subsequentes na
automação. Para mais detalhes, consulte
Execução condicional.
Fluxos aninhados
Em automações complexas, os nós de fluxo de execução também podem ser aninhados. Por exemplo, você pode ter um fluxo sequencial que executa um fluxo paralelo.
Os nós de DSL podem ser aninhados e combinados de várias maneiras para atender às suas necessidades específicas, de acordo com as restrições descritas na tabela a seguir. A coluna "Builder" tem links para a documentação do builder com segurança de tipo do Kotlin, que detalha o que é permitido para uso em cada tipo de nó.
| Nó | Pode conter o seguinte tipo de nó e dados | Precisa estar em um dos seguintes tipos de nó |
|---|---|---|
| Starter | Expressão | Selecionar, Sequencial |
| ManualStarter | Selecionar, Sequencial | |
| StateReader | Expressão (normalmente consiste em um valor de atributo de característica) | IfFlow, Parallel, Sequential |
| Ação | Comando, entidade, expressão | IfFlow, Parallel, Select, Sequential |
| Sequencial | IfFlow, Parallel, Select, Sequential | |
| Paralela | Ação | IfFlow, Sequential |
| Condição | Expressão | IfFlow, Parallel, Sequential |
| Selecionar | Condition, Sequential, Starter, ManualStarter | Sequencial e precisa ser o primeiro nó no fluxo |
| IfFlow | Action, Condition, Delay, IfFlow, Parallel, Sequential, StateReader, Suppression | Sequencial |
DSL de automação
Nas APIs Home, as automações são definidas usando a DSL (linguagem específica de domínio) de automação. A DSL de automação é implementada como uma DSL do Kotlin (linguagem de domínio específico), usando builders com segurança de tipos do Kotlin, e é projetada especificamente para definir modelos de automação.
Quando uma automação é compilada, os builders de segurança de tipos do Kotlin geram classes de dados do Kotlin que são serializadas para o JSON do buffer de protocolo, que é usado para fazer chamadas para os serviços de automação do Google.
A DSL de automação simplifica e otimiza o processo de criação de automações. Ele usa nativamente o mesmo modelo de dados de atributos padrão do Matter e atributos do smart home incluídos na API Device.
A DSL de automação também define a lógica de uma automação em termos de tipos de dispositivos abstratos, em vez de instâncias de dispositivos específicos localizadas na casa de um usuário. Ele permite que o desenvolvedor forneça parâmetros de entrada que podem ser usados no ambiente de execução para especificar instâncias reais de dispositivos, além de outros valores de parâmetros importantes.
A sintaxe da DSL é semelhante à do Kotlin e tem a mesma segurança de tipos, mas uma automação escrita na DSL de automação é mais simples e concisa do que a mesma automação escrita em Kotlin puro.
Exemplo
Veja um exemplo de automação que liga um dispositivo, escrita usando a DSL de automação:
val automation = automation {
name = "MyFirstAutomation"
description = "If light1 is on, turn on light2."
isActive = true
sequential {
val onOffTrait = starter<_>(device1, OnOffLightDevice, OnOff)
condition() { expression = onOffTrait.onOff equals true }
action(device2, OnOffLightDevice) { command(OnOff.on()) }
}
}
Essa automação é muito básica: quando device1, uma luz, é acesa (o atributo onOff
muda para true), ela envia o comando on() para ativar
device2.
A automação usa um nó sequential, o que indica que os nós serão executados
em ordem sequencial.
No nó sequential, há nós comportamentais, como starter,
condition e action. A saída do nó starter é atribuída a uma
variável para uso no nó condition.