Руководство по Android DSL

В этом руководстве рассказывается, как использовать различные узлы DSL автоматизации для создания автоматизации.

Весь DSL автоматизации размещается в одном узле automation. Узел automation формирует границу между внешним контекстом языка Kotlin и встроенным контекстом DSL.

Последовательный поток

Последовательный процесс автоматизации используется по умолчанию.

Пример последовательного DSL

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


import com.google.home.automation.action
import com.google.home.automation.automation
import com.google.home.automation.condition
import com.google.home.automation.sequential
import com.google.home.automation.starter

...

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

Вы можете добавить дополнительные узлы.

Для начинающих

Узлы условий определяют начальные обстоятельства, которые активируют автоматизацию. Например, изменение состояния или значения. В автоматизации должно быть хотя бы одно условие, иначе она не пройдет проверку. Чтобы добавить в автоматизацию несколько условий запуска, используйте узел select.

Стартовое условие на основе атрибута свойства

При объявлении начального узла, основанного на атрибуте признака, укажите:

  • устройство
  • тип устройства, к которому относится свойство;
  • трейт
starter<_>(thermostat, TemperatureSensorDevice, TemperatureMeasurement)

Параметр device type обязателен, поскольку он позволяет указать, к какому типу устройств относится автоматизация. Например, устройство может состоять из FanDevice и HeatingCoolingUnitDevice, и оба они содержат типаж OnOff. Указав тип устройства, вы точно определите, какая его часть запускает автоматизацию.

Стартовое условие на основе события

При объявлении начального узла на основе события укажите:

  • устройство
  • тип устройства, к которому относится свойство;
  • мероприятие
starter<_>(doorBell, GoogleDoorbellDevice, DoorbellPressed)

Стартовый шаблон на основе структуры и события с параметрами

У некоторых событий могут быть параметры, которые также нужно включить в стартер.

Например, это условие использует ScheduledTimeEvent свойства Time, чтобы запустить автоматизацию в 7:00:

val earlyMorning = starter<_>(structure, Time.ScheduledTimeEvent) {
  parameter(Time.ScheduledTimeEvent.clockTime(
    LocalTime.of(7, 0, 0, 0)))
}

Триггер на основе погоды

Вы можете указать текущие или прогнозируемые погодные условия в стартовом запросе, используя типаж "Погода":

val weatherState = starter<_>(structure, trait = Weather)

См. пример Закрыть жалюзи, если возможен дождь на странице Примеры автоматизаций.

Ручной стартер

Условие запуска вручную – это особый тип условия, который позволяет пользователю запускать автоматизацию вручную.

При объявлении условия запуска вручную:

  • Не указывайте тип устройства или его характеристики.
  • Добавьте элемент интерфейса, который вызывает Automation.execute().

Если в автоматизации select есть условие запуска вручную и другое условие, то условие запуска вручную переопределяет другое условие.

select {
  manualStarter()
  starter<_>(thermostat, TemperatureSensorDevice, TemperatureMeasurement)
}

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

Как разделить условие запуска вручную и условное условие запуска

Чтобы узлы condition не блокировали автоматизацию, запущенную вручную, можно поместить другой стартер в отдельный последовательный поток вместе с его condition:

automation_graph {
  sequential {
    select {
      sequential {
        starter<_>(...)
        condition {...}
      }
      sequential {
        manualStarter()
      }
    }
    action {...}
  }
}

Как ссылаться на значение атрибута

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

С stateReader:

val time = stateReader<_>(structure, Structure, Time)
val currTime = time.currentTime

С starter:

val starterNode = starter<_>(device1, LaundryWasherDevice, OnOff)
condition() {
  expression = starterNode.onOff equals true
}

Узлы и выражения условий

Узел условия представляет собой точку принятия решения, которая определяет, продолжается ли автоматизация. В автоматизации может быть несколько узлов condition. Если выражение любого узла condition оценивается как false, выполнение всей автоматизации прекращается.

В узле condition можно объединить несколько критериев условий, используя различные операторы, если выражение оценивается как одно логическое значение. Если полученное значение – true, условие выполнено и автоматизация переходит к следующему узлу. Если значение равно false, автоматизация прекращает выполняться.

Выражения формируются так же, как и в Kotlin, и могут содержать примитивные значения, такие как числа, символы, строки и логические значения, а также значения Enum. Группировка подвыражений с помощью круглых скобок позволяет управлять порядком их вычисления.

Вот пример condition, в котором несколько подвыражений объединены в одно:

condition() {
  val expr1 = starterNode.lockState equals DlLockState.Unlocked
  val expr2 = stateReaderNode.lockState equals true

  val expr3 = occupancySensingDevice.occupied notEquals 0
  val expr4 = timeStateReaderNode
    .currentTime
    .between(
      timeStateReaderNode.sunsetTime,
      timeStateReaderNode.sunriseTime)
  expression = (expr1 and expr2) or (expr3 and expr4)
}

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

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

stateReader

Другой способ сослаться на значения атрибутов черты в узле condition – использовать узел stateReader.

Для этого сначала запишите значение атрибута trait в узел stateReader. stateReader принимает structure и признак в качестве аргументов:

import com.google.home.automation.stateReader
...
val filterMonitoringState = stateReader<_>(structure, ActivatedCarbonFilterMonitoring)

Затем укажите ссылку на stateReader в узле condition:

condition() {
  expression =
    filterMonitoringState.changeIndication
      .equals(ChangeIndicationEnum.Warning)
}

С помощью операторов сравнения и логических операторов в узле condition можно использовать несколько элементов stateReaders:

val armState = stateReader<_>(doorLock, DoorLockDevice, ArmDisarm )
val doorLockState = stateReader<_>(doorLock, DoorLockDevice, DoorLock)
condition() {
  expression =
    (armState.armState equals true)
    and
    (doorLockState.lockState equals true)
}

Длительность условия

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

  condition {
    expression(lightStateReader.onOff == true)
    forDuration(Duration.ofMinutes(10))
  }

Продолжительность может составлять от 5 секунд до 24 часов.

Узлы действий

Узел действия – это место, где выполняется работа автоматизации. В этом примере действие вызывает команду broadcast() типажа AssistantBroadcast:

action(device, SpeakerDevice) {
  command(AssistantBroadcast.broadcast("Intruder detected!"))
}

Операторы import

При разработке автоматизаций не всегда очевидно, как импортировать различные элементы Home API в код.

Атрибуты признака импортируются из объекта Companion признака:

import com.google.home.matter.standard.OnOff.Companion.onOff

Структуры данных, определяемые признаком, импортируются из класса признака, название которого заканчивается на "-Trait":

import com.google.home.matter.standard.MediaPlaybackTrait.PlaybackStateEnum

Команды импортируются из объекта Companion типажа:

import com.google.home.matter.standard.Thermostat.Companion.setTemperatureSetpointHold