Conceptos de DSL de Android

Esta es una descripción general de los conceptos fundamentales de la DSL de Automation en Android.

Componentes de automatización

Una automatización consta de los siguientes componentes básicos, que suelen evaluarse en este orden:

  1. Starter : Define las condiciones iniciales que activan la automatización, como un cambio en un rasgo. Una automatización debe tener un starter.
  2. Condition : Son las restricciones adicionales que se deben evaluar después de que se activa una automatización. La expresión de una condición debe evaluarse como true para que se realicen las acciones de una automatización.
  3. Action : Son los comandos o las actualizaciones de estado que se realizan cuando se cumplen todas las condiciones.

Por ejemplo, supongamos que tienes una automatización que atenúa las luces de una habitación cuando se enciende la TV de esa habitación entre el atardecer y el amanecer. En este ejemplo:

  1. Starter : Se encendió la TV, lo que representa un cambio de estado en un rasgo de TV.
  2. Condición: Se evalúa la hora actual de la casa en la que se encuentra la TV.
  3. Action : Se atenúan las luces de la misma habitación que la TV.

La automatización se activará cuando se encienda la TV de la habitación, pero solo se ejecutará si se cumple la condición de que la hora esté entre el atardecer y el amanecer.

Además de la estructura básica, las automatizaciones de las APIs de Home también contienen metadatos, como name y description, que se pueden usar para identificar them para desarrolladores y usuarios.

Nodos

En las APIs de Home, la estructura lógica de una automatización consta de nodos. Los nodos son unidades abstractas y reutilizables que representan comportamientos de entidades o flujos de ejecución. Cada nodo puede tener variables de entrada, así como variables de salida que pueden consumir otros nodos.

Tabla: Tipos de nodos de automatización
Nodo Tipo de nodo Implementación de Kotlin Descripción
Starter Comportamiento StarterNodeDsl Inicia una automatización cuando cambia el estado de un rasgo (cualquier atributo).
StateReader Comportamiento StateReaderNodeDsl Lee un atributo de rasgo y te permite capturar su valor para usarlo en nodos de condición.
Action Comportamiento ActionNodeDsl Invoca comandos de rasgo.
Sequential Flujo de ejecución SequentialFlow Ejecuta nodos de acción anidados en secuencia. Este es el comportamiento de ejecución predeterminado.
Parallel Flujo de ejecución ParallelFlow Ejecuta nodos de acción anidados en paralelo.
Condition Flujo de ejecución ConditionNodeDsl Cambia de forma condicional el flujo de ejecución en función de las evaluaciones de expresiones lógicas. Las condiciones pueden asociarse con un starter (condiciones específicas del starter ) o ser globales (se aplican a todos los starters).
Select Flujo de ejecución SelectFlow Permite que más de un starter active una automatización.
Expression Valor Expression Puede ser el valor de un atributo de rasgo, una constante o un valor literal, y debe evaluarse como una lista, un número, un valor booleano o una cadena.

Nodos de comportamiento

Los nodos, como los starters y las acciones, son nodos de comportamiento. Los starters activan una automatización en función de los cambios en los atributos del dispositivo. Las acciones emiten comandos del dispositivo o actualizan atributos.

Por lo general, los nodos de comportamiento están vinculados a los rasgos del dispositivo y al estado del rasgo de salida para usarlos como entrada en otros nodos.

Nodos de flujo de ejecución

Algunos nodos representan flujos de ejecución, como los secuenciales y los paralelos. Cada uno de estos nodos contiene los nodos de comportamiento que definen la automatización.

Por ejemplo, un flujo secuencial puede contener nodos que se ejecutan en orden secuencial. Por lo general, estos serían starter, condición y acción.

Flujos de ejecución secuenciales
Figura 1: Flujo de automatización secuencial

Un flujo paralelo puede tener varios nodos de acción que se ejecutan al mismo tiempo, como encender varias luces al mismo tiempo. Los nodos que siguen un flujo paralelo no se ejecutarán hasta que finalicen todas las ramas del flujo paralelo.

Flujos de ejecución paralela
Figura 2: Flujo de automatización paralelo

Otro tipo de flujo de ejecución es un flujo de condición, que puede cambiar el flujo de ejecución en función de la evaluación de una expresión.

Por ejemplo, supongamos que tienes una automatización que realiza una acción según si es de noche. Un nodo de condición verifica la hora del día y, luego, sigue la ruta de ejecución adecuada en función de esa evaluación.

Flujo de condición
Figura 3: Flujo de condición

Un flujo de selección es útil cuando deseas tener más de un starter que pueda activar tu automatización. Cuando incluyes dos o más starters en un flujo select, cualquiera de ellos puede activar la automatización.

Por ejemplo, puedes escribir una automatización que baje las persianas al atardecer, si la temperatura supera un umbral determinado o si el brillo supera un umbral. Tres starters independientes controlan cada una de estas situaciones, y los tres se incluirían en un flujo select.

Selecciona el flujo.
Figura 4: Flujo de selección

Flujos anidados

En las automatizaciones complejas, también se pueden anidar los nodos de flujo de ejecución. Por ejemplo, puedes tener un flujo secuencial que ejecute un flujo paralelo.

Flujos de ejecución anidados
Figura 5: Flujos de ejecución anidados

Los nodos DSL se pueden anidar y combinar de varias maneras para satisfacer tus necesidades específicas, según las restricciones que se describen en la siguiente tabla. La columna Builder vincula a la documentación del compilador de acceso seguro a tipos de Kotlin, que detalla lo que se permite usar en cada tipo de nodo.

Tabla: Cómo se pueden combinar los nodos
Nodo Puede contener el siguiente tipo de nodo y datos Debe estar dentro de uno de los siguientes tipos de nodos
Starter Expresión Seleccionar, secuencial
ManualStarter Seleccionar, secuencial
StateReader Expresión (que suele constar de un valor de atributo de rasgo) Acción, condición
Action Comando, entidad, expresión Paralelo, seleccionar, secuencial
Sequential Paralelo, seleccionar, secuencial
Parallel Acción Secuencial
Condition Expresión Paralelo, secuencial
Select Condición, secuencial, starter, ManualStarter Secuencial y debe ser el primer nodo del flujo

DSL de Automation

En las APIs de Home, las automatizaciones se definen con la DSL de Automation (lenguaje específico del dominio). La DSL de Automation se implementa como una DSL de Kotlin (lenguaje específico del dominio), con compiladores de acceso seguro a tipos de Kotlin y está diseñada específicamente para definir plantillas de automatización.

Cuando se compila una automatización, los compiladores de acceso seguro a tipos de Kotlin generan clases de datos de Kotlin que luego se serializan en JSON de búfer de protocolo, que se usa para realizar llamadas a los servicios de automatización de Google.

La DSL de Automation simplifica y optimiza el proceso de creación de automatizaciones. Usa de forma nativa el mismo modelo de datos de Matter rasgos estándar y smart home rasgos que se incluyen en la API de Device.

La DSL de Automation también define la lógica de una automatización en términos de tipos de dispositivos abstractos, en lugar de instancias de dispositivos específicos ubicados en la casa de un usuario. Permite que el desarrollador proporcione parámetros de entrada que se pueden usar en el tiempo de ejecución para especificar instancias de dispositivos reales, así como otros valores de parámetros importantes.

La sintaxis de DSL es similar a la de Kotlin y es igualmente segura para tipos, pero una automatización escrita en la DSL de Automation es más simple y concisa que la misma automatización escrita en Kotlin puro.

Ejemplo

A continuación, se muestra un ejemplo de automatización que enciende un dispositivo, escrito con la DSL de Automation:

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()) }
  }
}

Esta automatización es muy básica: cuando device1, una luz, se enciende (el atributo onOff cambia a true), envía el comando on() para encender device2.

La automatización usa un nodo sequential, que indica que sus nodos se ejecutarán en orden secuencial.

Dentro del nodo sequential, hay nodos de comportamiento como starter, condition y action. La salida del nodo starter se asigna a una variable para usarla en el nodo condition.