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

Следуйте приведенному ниже руководству, чтобы понять, как различные узлы Automation 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)

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

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

Продолжительность может варьироваться от одной до 30 минут.

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

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

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

Импорт операторов

При разработке автоматизации не всегда очевидно, как импортировать различные элементы API 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