Guía de la DSL de iOS

Usa la siguiente guía para comprender cómo se pueden usar varios nodos de DSL de automatización para construir una automatización.

Toda la DSL de automatización se coloca dentro de un solo nodo automation. El nodo automation forma el límite entre el contexto del lenguaje Swift externo y el contexto de DSL incorporado.

Flujo secuencial

El flujo secuencial es el tipo predeterminado de flujo de automatización.

Ejemplo de DSL secuencial

Esta es una plantilla de DSL de automatización muy básica que usa un flujo secuencial que consta de un activador, una condición y una acción:

import GoogleHomeSDK
import GoogleHomeTypes

automation (
...
) {
  starter(...)
  condition {...}
  action {...}
}

Para definir mejor el modelo, puedes agregar nodos adicionales.

Starter

Los nodos de activador definen las circunstancias iniciales que activan una automatización. Por ejemplo, un cambio de estado o valor. Una automatización debe tener al menos un activador. De lo contrario, no aprobará la validación. Para agregar más de un activador a una automatización, debes usar un nodo select.

Activador basado en el atributo trait

Cuando declares un nodo de partida que se base en un atributo de rasgo, especifica lo siguiente:

  • el dispositivo
  • el tipo de dispositivo al que pertenece el atributo
  • el rasgo
starter(
  thermostat,
  Matter.TemperatureSensorDeviceType.self,
  Matter.TemperatureMeasurementTrait.self
)

El parámetro de tipo de dispositivo es obligatorio porque te permite especificar a qué tipo de dispositivo se dirige la automatización. Por ejemplo, un dispositivo puede estar compuesto por un FanDeviceType y un HeatingCoolingUnitDeviceType, ambos con el atributo OnOffTrait. Cuando especificas el tipo de dispositivo, no hay ambigüedades sobre qué parte del dispositivo activa la automatización.

Activador basado en el evento

Cuando declares un nodo de activación basado en un evento, especifica lo siguiente:

  • el dispositivo
  • el tipo de dispositivo al que pertenece el atributo
  • el evento
starter(
  doorbell,
  Google.GoogleDoorbellDeviceType.self,
  Google.DoorbellPressTrait.DoorbellPressedEvent
)

Activador basado en una estructura y un evento, con parámetros

Algunos eventos pueden tener parámetros, por lo que estos también deben incluirse en el activador.

Por ejemplo, este activador usa el ScheduledEvent de TimeTrait para activar la automatización a las 7:00 a.m.:

typealias TimeTrait = Google.TimeTrait

let earlyMorning = starter(
  structure,
  TimeTrait.ScheduledEvent.self
) {
  TimeTrait.ScheduledEvent.clockTime(TimeOfDay(hours: 7, minutes: 0))
}

Arranque manual

Un activador manual es un tipo especial de activador que permite al usuario ejecutar la automatización de forma manual.

Cuando declares un activador manual, haz lo siguiente:

  • No especifiques un tipo de dispositivo ni una característica.
  • Proporciona un elemento de la IU que llame a Automation.execute().

Cuando colocas un activador manual en un flujo select junto con otro activador, el activador manual anula el otro:

select {
  manualStarter()
  starter(
    thermostat,
    Matter.TemperatureSensorDeviceType.self,
    Matter.TemperatureMeasurementTrait.self
  )
}

Ten en cuenta que se evaluarán todos los nodos condition que sigan a un activador manual y que podrían bloquear la ejecución de la automatización, según la expresión condition.

Separa un activador manual de uno condicional

Una forma de estructurar tu automatización para que los nodos condition no bloqueen una automatización que se activó con un activador manual es colocar el otro activador en un flujo secuencial independiente junto con su condition:

import GoogleHomeSDK
import GoogleHomeTypes

automation (
...
) {

  select {
    sequential {
      starter(...)
      condition {...}
    }
    sequential {
      manualStarter()
    }
  }
  action {...}

}

Cómo hacer referencia al valor de un atributo

Para usar el valor de un atributo en una expresión, usa la siguiente sintaxis.

Con un stateReader, haz lo siguiente:

typealias TimeTrait = Google.TimeTrait

let time = stateReader(structure, TimeTrait.self)
time
let currTime = time.currentTime

Con un starter, haz lo siguiente:

typealias LaundryWasherDeviceType = Matter.LaundryWasherDeviceType
typealias OnOffTrait = Google.OnOffTrait

let starterNode = starter(device1, LaundryWasherDeviceType.self, OnOffTrait.self)
starterNode
condition {
  starterNode.onOff.equals(true)
}

Nodos y expresiones de condiciones

Un nodo de condición representa un punto de decisión que determina si la automatización continúa o no. Una automatización puede tener varios nodos condition. Si la expresión de cualquier nodo condition se evalúa como false, finaliza la ejecución de toda la automatización.

Dentro de un nodo condition, puedes combinar varios criterios de condición con varios operadores, siempre que la expresión se evalúe como un solo valor booleano. Si el valor resultante es true, se cumple la condición y la automatización continúa con la ejecución del próximo nodo. Si es false, la automatización dejará de ejecutarse en ese momento.

Las expresiones se forman de manera similar a las de Swift y pueden contener valores primitivos, como números, caracteres, cadenas y valores booleanos, así como valores de Enum. Agrupar subexpresiones con paréntesis te permite controlar el orden en el que se evalúan.

Este es un ejemplo de un condition que combina varias subexpresiones en una sola expresión:

condition {
  let exp1 = starterNode.lockState.equals(.unlocked)
  let exp2 = stateReaderNode.lockState.equals(true)
  let exp3 = occupancySensingDevice.occupied.notEquals(0)
  (exp1.and(exp2)).or(exp3)
}

Puedes hacer referencia al valor de un atributo al que se accede a través de un activador:

typealias OnOffTrait = Matter.OnOffTrait

let starterNode = starter(device, OnOffTrait.self)
starterNode
condition {
  starterNode.onOff.equals(true)
}
val starterNode = starter<_>(device, OnOff)
condition() { expression = starterNode.onOff equals true }

stateReader

La otra forma de hacer referencia a los valores de los atributos de los rasgos en un nodo condition es con un nodo stateReader.

Para ello, primero captura el valor del atributo del rasgo en un nodo stateReader. Un stateReader toma el structure y el atributo como argumentos:

typealias ActivatedCarbonFilterMonitoringTrait = Matter.ActivatedCarbonFilterMonitoringTrait

let filterMonitoringState = stateReader(structure, ActivatedCarbonFilterMonitoringTrait.self)

Luego, haz referencia a stateReader en el nodo condition:

condition {
filterMonitoringState.changeIndication.equals(.warning)
}

Con la comparación y los operadores lógicos, se pueden usar varios stateReaders en un nodo condition:

typealias ArmDisarm = Google.ArmDisarmTrait
typealias DoorLockDevice = Matter.DoorLockDeviceType
typealias DoorLock = Matter.DoorLockTrait

let armState = stateReader(doorLock, DoorLockDevice.self, ArmDisarm )
let doorLockState = stateReader(doorLock, DoorLockDevice.self, DoorLock)
armState
doorLockState
condition {
  let exp1 = armState.armState
  let exp2 = doorLockState.lockState
  exp1.and(exp2)
}

Duración de la condición

Además de una expresión booleana en una condición, puedes especificar un período durante el cual la expresión debe ser verdadera para ejecutar la automatización. Por ejemplo, puedes definir una condición que se active solo si una luz está encendida durante diez minutos.

condition(for: .seconds(600)) {
lightStateReader.onOff.equals(true)
}

La duración puede variar de uno a 30 minutos.

Nodos de acción

El nodo de acción es donde se realiza el trabajo de la automatización. En este ejemplo, la acción invoca el comando broadcast() de AssistantBroadcastTrait:

action(speaker, SpeakerDeviceType.self) {
  Google.AssistantBroadcastTrait.broadcast(msg: "Oven Cycle Complete")
}