Руководство по Android DSL для сложных автоматизаций

DSL-скрипты позволяют создавать более сложные автоматизации, чем те, которые описаны в руководстве по DSL-скриптам для базовых автоматизаций на Android.

Последовательные с несколькими действиями

Последовательные с несколькими действиями

Автоматизация может выполнять несколько действий. Например, вместо одного узла action можно использовать несколько узлов action, которые будут выполняться последовательно:

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

Последовательные действия с несколькими параллельными действиями

Последовательные с несколькими параллельными действиями

Если вы поместите несколько узлов action в узел parallel, действия будут выполняться одновременно.

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

Если в узле sequential есть узлы action, которые идут после узла parallel, они не будут выполняться, пока не завершится выполнение всех узлов в узле parallel.

Условное выполнение

По умолчанию автоматизация выполняет узлы последовательно или параллельно. Если вам нужна логика условного ветвления, то есть выполнение разных действий или путей в зависимости от условий выполнения, используйте операторы if-then-else, которые создаются с помощью блоков DSL условного управления: ifThen, elseIf и orElse.

Стандартный узел condition блокирует всю автоматизацию (если условие оценивается как false, автоматизация немедленно прекращает выполнение), а блоки ifThen позволяют управлять ветвлением.

  • Узел condition останавливает выполнение всей автоматизации (или текущего пути выполнения), если выражение равно false.
  • ifThen / elseIf / orElse – оценивает условия по порядку. Если условие false, выполнение переходит к следующей ветви elseIf, резервной ветви orElse или к последующим узлам автоматизации, если ни одно условие не выполняется.

Выполнение действия на основе условия

Блок ifThen оценивает условное выражение. Если выражение оценивается как true, выполняются действия или узлы DSL, вложенные в блок. Если условие не выполняется (false), действия пропускаются и автоматизация переходит к следующим узлам в последовательности.

Вы можете использовать отдельный блок ifThen, если хотите выполнить действие при условии, не блокируя и не завершая последующие узлы в автоматизации:

// 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."))
    }
  }
}

Как выполнять разные действия в зависимости от условия

Чтобы выполнить один набор действий, если условие истинно, и другой, если оно ложно, добавьте блок .orElse { ... } после 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!"))
      }
    }
  }
}

Объединение нескольких условий в последовательность

Вы можете объединить один или несколько необязательных блоков .elseIf(...) { ... }, чтобы последовательно оценить несколько условий. Выполняется первая ветвь, условие которой оценивается как true, а все остальные ветви пропускаются. Если ни одно из условий не равно true, выполняется необязательный блок .orElse { ... } (если он указан):

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

Вложенные условные процессы

Условные блоки можно вкладывать в другие блоки ifThen, elseIf или orElse, а также комбинировать с узлами parallel, delayFor и stateReader.

Блоки ifThen, elseIf и orElse выполняют свое содержимое как последовательные потоки. Внутри каждой ветви можно разместить любые последовательные узлы, включая action, stateReader, parallel, delayFor и вложенные блоки ifThen.

Задержка обработки платежа

Вы можете добавлять паузы в автоматизации с помощью ключевого слова delayFor, которое принимает аргумент java.time.Duration, указывающий, как долго нужно ждать перед продолжением выполнения. Продолжительность паузы может составлять от пяти секунд до 24 часов.

Например, чтобы включить и выключить свет четыре раза с паузой в пять секунд между каждым переключением:

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

Исключение триггеров

Подавление триггера – это функция, которая позволяет автоматизации игнорировать starter в течение определенного периода времени после первоначального события-триггера. Например, если автоматизация содержит starter, который срабатывает при обнаружении движения, и вы задали длительность подавления триггера в пять минут, то после срабатывания starter он не сработает снова в течение следующих пяти минут. Это предотвращает быстрое повторное срабатывание автоматизации.

Чтобы применить подавление триггеров к автоматизации, используйте ключевое слово suppressFor с аргументом java.time.Duration, который указывает, сколько времени должно пройти, прежде чем автоматизация сможет реагировать на последующие триггеры. Продолжительность блокировки может составлять от пяти секунд до 24 часов.

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

Обратите внимание, что подавление триггеров влияет на все starters в автоматизации, которые предшествуют suppressFor.

Ограничьте количество выполнений

Вы можете ограничить количество запусков автоматизации.

Например, вы можете настроить однократную автоматизацию, которая запустит пылесос, пока вас не будет дома.

Для этого задайте поле метаданных maxExecutionCount автоматизации. Ниже приведен пример автоматизации, которая может выполняться только один раз.

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

Автоматизация будет удалена сразу после последнего выполнения, когда будет достигнуто значение maxExecutionCount. Запись об автоматизации останется на вкладке Google Home app (GHA) Действия, в том числе в разделе automation_id.

Как задать атрибуты признака в действии

Чтобы задать значение атрибута, выполните следующие действия:

  1. Создайте узел update внутри узла action, указав нужный признак в качестве аргумента узла update:
    action(deviceReference, deviceType) {
      update(trait) {
    
      }
    }
  2. В узле update для каждого атрибута, который нужно изменить, используйте функцию-мутатор и передайте ей новое значение. Чтобы сформировать название функции-мутатора:
    1. Название атрибута должно начинаться с заглавной буквы.
    2. Добавьте к нему префикс set.
    Например, чтобы обновить атрибут defaultMoveRate, нужно использовать функцию-мутатор setDefaultMoveRate.

Обратите внимание, что у узла update может быть несколько функций-мутаторов. Ниже приведен пример, в котором обновляются два атрибута:

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