Guía de DSL de Android para automatizaciones complejas

Se puede usar la DSL de automatización para crear automatizaciones más complejas que las que se describen en la guía de DSL: automatizaciones básicas en Android.

Secuencial con varias acciones

Secuencial con varias acciones

Una automatización puede hacer más de una cosa. Por ejemplo, en lugar del nodo action único, podrías tener varios nodos action, que se ejecutan en orden secuencial:

automation {
  sequential {
    starter<_>(...)
    condition {...}
    action {...}
    action {...}
    action {...}
    }
}

Secuencial con varias acciones paralelas

Secuencial con varias acciones paralelas

Si colocas varios nodos action en un nodo parallel, las acciones se ejecutan de forma simultánea.

automation {
  sequential {
    starter<_>(...)
    condition {...}
    parallel {
      action {...}
      action {...}
      action {...}
    }
  }
}

Si hay nodos action en el nodo sequential que vienen después del nodo parallel, esperan a ejecutarse hasta que todos los nodos dentro del nodo parallel hayan terminado de ejecutarse.

Ejecución condicional

De forma predeterminada, una automatización ejecuta nodos de forma secuencial o en paralelo. Si necesitas una lógica de ramificación condicional (ejecutar diferentes acciones o rutas según las condiciones de tiempo de ejecución), usa sentencias if-then-else, que se compilan con los bloques de DSL de flujo de control condicional: ifThen, elseIf, y orElse.

Si bien un nodo condition estándar limita toda la automatización (si la condición se evalúa como false, la automatización finaliza la ejecución de inmediato), los bloques ifThen permiten el flujo de control de ramificación:

  • Nodo condition: Detiene la ejecución de toda la automatización (o la ruta de ejecución actual) si la expresión es false.
  • ifThen / elseIf / orElse: Evalúa las condiciones en orden. Si una condición es false, la ejecución pasa a la siguiente rama elseIf, a la rama de resguardo orElse o continúa con los nodos posteriores de la automatización si no se cumple ninguna condición.

Ejecuta una acción según una condición

El bloque ifThen evalúa una expresión condicional. Si la expresión se evalúa como true, se ejecutan las acciones o los nodos de DSL anidados dentro del bloque. Si se evalúa como false, se omiten las acciones y la automatización continúa con los nodos posteriores en el flujo secuencial.

Puedes usar un bloque ifThen independiente cuando solo deseas realizar una acción de forma condicional sin bloquear ni finalizar los nodos posteriores en la automatización:

// When a door opens, turn on the light only if it is off,
// and always broadcast an announcement.
automation {
  sequential {
    val contactState = starter<_>(
      contactSensor,
      ContactSensorDevice,
      BooleanState,
    )
    val lightState = stateReader<_>(
      light,
      DimmableLightDevice,
      OnOff,
    )

    condition {
      // Door opened (contact sensor open)
      expression = contactState.stateValue equals false
    }

    // Conditionally turn on the light if it's off
    ifThen(lightState.onOff equals false) {
      action(light, DimmableLightDevice) { command(OnOff.on()) }
    }

    // Continue executing subsequent actions in the sequential flow
    action(structure) {
      command(AssistantBroadcast.broadcast("The door was opened."))
    }
  }
}

Ejecuta diferentes acciones según una condición

Para ejecutar un conjunto de acciones cuando una condición es verdadera y un conjunto alternativo de acciones cuando es falsa, encadena el bloque .orElse { ... } opcional después de ifThen(...) { ... }:

// When the door is unlocked, turn on the entryway light if it is off;
// otherwise, broadcast a welcome message.
automation {
  sequential {
    val doorLockEvent = starter<_>(
      doorLock,
      DoorLockDevice,
      LockOperationEvent,
    )
    val lightState = stateReader<_>(
      light,
      DimmableLightDevice,
      OnOff,
    )

    condition {
      expression =
        doorLockEvent.lockOperationType equals LockOperationTypeEnum.Unlock
    }

    ifThen(lightState.onOff equals false) {
      action(light, DimmableLightDevice) { command(OnOff.on()) }
    }.orElse {
      action(structure) {
        command(AssistantBroadcast.broadcast("Welcome home!"))
      }
    }
  }
}

Encadena varias condiciones en secuencia

Puedes encadenar uno o más bloques .elseIf(...) { ... } opcionales para evaluar varias condiciones en secuencia. Se ejecuta la primera rama cuya condición se evalúa como true y se omiten todas las ramas restantes. Si ninguna de las condiciones se evalúa como true, se ejecuta un bloque .orElse { ... } opcional (si se proporciona):

// Adjust climate controls based on room temperature changes.
automation {
  sequential {
    val tempStarter = starter<_>(
      tempSensor,
      TemperatureSensorDevice,
      TemperatureMeasurement,
    )

    // If temperature is high (>= 28°C / 2800 mC), switch thermostat to Cool mode
    ifThen(tempStarter.measuredValue greaterThanOrEquals 2800) {
      action(thermostat, ThermostatDevice) {
        command(
          SimplifiedThermostat.setSystemMode(
            SimplifiedThermostatSystemModeEnum.Cool
          )
        )
      }
    }.elseIf(tempStarter.measuredValue lessThan 1800) {
      // If temperature is low (< 18°C / 1800 mC), switch to Heat mode
      action(thermostat, ThermostatDevice) {
        command(
          SimplifiedThermostat.setSystemMode(
            SimplifiedThermostatSystemModeEnum.Heat
          )
        )
      }
    }.orElse {
      // Otherwise, turn on the fan
      action(fan, FanDevice) {
        command(OnOff.on())
      }
    }
  }
}

Flujos condicionales anidados

Los bloques condicionales se pueden anidar dentro de otros bloques ifThen, elseIf o orElse, así como combinarse con nodos parallel, delayFor y stateReader.

Los bloques ifThen, elseIf y orElse ejecutan su contenido como flujos secuenciales. Puedes colocar cualquier nodo secuencial dentro de cada rama, incluidos los bloques action, stateReader, parallel, delayFor y ifThen anidados.

Demoras

Puedes introducir pausas en tus automatizaciones con la delayFor palabra clave, que toma un java.time.Duration argumento que representa cuánto tiempo se debe pausar antes de continuar con la ejecución. La duración de la pausa puede ser de cinco segundos o de 24 horas.

Por ejemplo, para alternar una luz cuatro veces con una pausa de cinco segundos entre cada alternancia, haz lo siguiente:

sequential {
  action(light, OnOffLightDevice) { command(OnOff.toggle()) }
  delayFor(Duration.ofSeconds(5))
  action(light, OnOffLightDevice) { command(OnOff.toggle()) }
  delayFor(Duration.ofSeconds(5))
  action(light, OnOffLightDevice) { command(OnOff.toggle()) }
  delayFor(Duration.ofSeconds(5))
  action(light, OnOffLightDevice) { command(OnOff.toggle()) }
}

Supresión de activadores

La supresión de activadores es una capacidad que permite que tu automatización ignore un starter durante un período especificado después del evento de activación inicial. Por ejemplo, si la automatización tiene un starter que se activa con la detección de movimiento y especificas una duración de supresión del activador de cinco minutos, cuando se active el starter, no se volverá a activar durante los próximos cinco minutos. Esto evita que la automatización se active rápidamente una y otra vez.

Para aplicar la supresión de activadores a tu automatización, usa la suppressFor palabra clave con un java.time.Duration argumento que represente cuánto tiempo esperar antes de responder a los activadores posteriores. La duración de la supresión puede ser de cinco segundos o de 24 horas.

automation {
  sequential {
    val starterNode = starter<_>(device, OccupancySensor, MotionDetection)
    suppressFor(Duration.ofMinutes(30))
    action(light, OnOffLightDevice) { command(OnOff.toggle()) }
}

Ten en cuenta que la supresión de activadores afecta a todos los starters de una automatización que preceden a suppressFor.

Limita la cantidad de ejecuciones

Puedes limitar la cantidad de veces que se permite ejecutar una automatización.

Por ejemplo, es posible que desees configurar una automatización única que ejecute la aspiradora mientras no estés en casa durante el día.

Para ello, establece el campo de metadatos maxExecutionCount de la automatización. En el siguiente ejemplo, se muestra una automatización que solo se puede ejecutar una vez:

automation {
  // The automation can only be executed once.
  maxExecutionCount = 1
  // When the door lock state changes
  sequential {
    val doorLockEvent = starter<_>(doorLock, DoorLockDevice, LockOperationEvent)
    // if the door is unlocked
    condition() {
      expression = (doorLockEvent.lockOperationType equals LockOperationTypeEnum.Unlock)
    }
    // turn the light on
    action(light, DimmableLightDevice) { command(OnOff.on()) }
  }
}

La automatización se borra de inmediato una vez que se completa la ejecución por última vez y se alcanza maxExecutionCount. La entrada del historial de la automatización permanece en la Google Home app (GHA) pestaña Actividad, incluido el automation_id.

Establece atributos de rasgos en una acción

Para establecer el valor de un atributo de rasgo, haz lo siguiente:

  1. Crea un nodo update dentro de un nodo action, incluido el rasgo pertinente como argumento para el nodo update:
    action(deviceReference, deviceType) {
      update(trait) {
    
      }
    }
  2. Dentro del nodo update, para cada atributo que se modificará, usa una función de mutador y pásale el valor nuevo. Para formar el nombre de la función de mutador, haz lo siguiente:
    1. Pon en mayúscula el nombre del atributo.
    2. Agrega el prefijo set.
    Por ejemplo, para actualizar un atributo llamado defaultMoveRate, usarías una función de mutador llamada setDefaultMoveRate.

Ten en cuenta que un nodo update puede tener varias funciones de mutador. Este es un ejemplo en el que se actualizan dos atributos:

action(device, Fan) {
  update(FanControl) {
    setPercentSetting(50u)
    setRockSetting(FanControlCluster.RockBitmap.rockUpDown)
  }
}