Руководство по 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
)

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