Conceitos de DSL do Android

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:

  1. 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.
  2. 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 true para que as ações de uma automação sejam executadas.
  3. 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:

  1. Ativador: a TV foi ligada, o que é uma mudança de estado em um atributo de TV.
  2. Condição: o horário atual da casa em que a TV está é avaliado.
  3. 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.

Tabela: tipos de nós de automação
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.

Fluxos de execução sequenciais
Figura 1: fluxo de automação sequencial

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.

Fluxos de execução paralela
Figura 2: fluxo de automação paralela

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.

Fluxo de condição
Figura 3: fluxo de condiçã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.

Selecionar fluxo
Figura 4: fluxo de seleção

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.

Fluxos de execução aninhados
Figura 5: fluxos de execução aninhados

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ó.

Tabela: como os nós podem ser combinados
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.