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

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

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

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

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

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

Вот простой шаблон Automation 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)

Параметр типа устройства обязателен, поскольку позволяет указать, к какому устройству в составе устройства относится автоматизация. Например, устройство может состоять из 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.

Для этого сначала получите значение атрибута признака в узле 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!"))
}

Операторы импорта

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

Атрибуты признака импортируются из объекта 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