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

Используйте следующее руководство, чтобы понять, как различные узлы Automation DSL могут быть использованы для построения автоматизации.

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

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

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

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

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

import GoogleHomeSDK
import GoogleHomeTypes

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

Его можно усовершенствовать, добавив дополнительные узлы.

Стартер

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

Стартер на основе атрибута черты характера

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

  • устройство
  • тип устройства, к которому принадлежит признак
  • черта
starter(
  thermostat,
  Matter.TemperatureSensorDeviceType.self,
  Matter.TemperatureMeasurementTrait.self
)

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

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

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

При объявлении ручного стартера:

  • Не указывайте характеристику или тип устройства.
  • Предоставьте элемент пользовательского интерфейса, который вызывает 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 .

Для этого сначала необходимо получить значение атрибута типажа в узле 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)
}

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

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

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

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