Руководство по DSL для iOS

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

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

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

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

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

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

import GoogleHomeSDK
import GoogleHomeTypes

automation (
...
) {
  starter(...)
  condition {...}
  action {...}
}

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

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

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

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

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

  • устройство
  • тип устройства, к которому относится свойство;
  • трейт
starter(
  thermostat,
  Matter.TemperatureSensorDeviceType.self,
  Matter.TemperatureMeasurementTrait.self
)

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

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

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

  • устройство
  • тип устройства, к которому относится свойство;
  • мероприятие
starter(
  doorbell,
  Google.GoogleDoorbellDeviceType.self,
  Google.DoorbellPressTrait.DoorbellPressedEvent
)

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

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

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

typealias TimeTrait = Google.TimeTrait

let earlyMorning = starter(
  structure,
  TimeTrait.ScheduledEvent.self
) {
  TimeTrait.ScheduledEvent.clockTime(TimeOfDay(hours: 7, minutes: 0))
}

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

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

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

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

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

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

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

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

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

select {
  manualStarter()
  starter(
    thermostat,
    Matter.TemperatureSensorDeviceType.self,
    Matter.TemperatureMeasurementTrait.self
  )
}

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

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

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

import GoogleHomeSDK
import GoogleHomeTypes

automation (
...
) {

  select {
    sequential {
      starter(...)
      condition {...}
    }
    sequential {
      manualStarter()
    }
  }
  action {...}

}

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

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

С stateReader:

typealias TimeTrait = Google.TimeTrait

let time = stateReader(structure, TimeTrait.self)
time
let currTime = time.currentTime

С starter:

typealias LaundryWasherDeviceType = Matter.LaundryWasherDeviceType
typealias OnOffTrait = Google.OnOffTrait

let starterNode = starter(device1, LaundryWasherDeviceType.self, OnOffTrait.self)
starterNode
condition {
  starterNode.onOff.equals(true)
}

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

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

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

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

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

condition {
  let exp1 = starterNode.lockState.equals(.unlocked)
  let exp2 = stateReaderNode.lockState.equals(true)
  let exp3 = occupancySensingDevice.occupied.notEquals(0)
  (exp1.and(exp2)).or(exp3)
}

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

typealias OnOffTrait = Matter.OnOffTrait

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

stateReader

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

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

typealias ActivatedCarbonFilterMonitoringTrait = Matter.ActivatedCarbonFilterMonitoringTrait

let filterMonitoringState = stateReader(structure, ActivatedCarbonFilterMonitoringTrait.self)

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

condition {
filterMonitoringState.changeIndication.equals(.warning)
}

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

typealias ArmDisarm = Google.ArmDisarmTrait
typealias DoorLockDevice = Matter.DoorLockDeviceType
typealias DoorLock = Matter.DoorLockTrait

let armState = stateReader(doorLock, DoorLockDevice.self, ArmDisarm )
let doorLockState = stateReader(doorLock, DoorLockDevice.self, DoorLock)
armState
doorLockState
condition {
  let exp1 = armState.armState
  let exp2 = doorLockState.lockState
  exp1.and(exp2)
}

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

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

condition(for: .seconds(600)) {
lightStateReader.onOff.equals(true)
}

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

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

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

action(speaker, SpeakerDeviceType.self) {
  Google.AssistantBroadcastTrait.broadcast(msg: "Oven Cycle Complete")
}