Przewodnik DSL na iOS

Z tego przewodnika dowiesz się, jak za pomocą różnych węzłów Automation DSL tworzyć automatyzacje.

Cały język DSL automatyzacji znajduje się w jednym węźle automation. Węzeł automation stanowi granicę między zewnętrznym kontekstem języka Swift a osadzonym kontekstem DSL.

Przepływ sekwencyjny

Przepływ sekwencyjny to domyślny typ przepływu automatyzacji.

Przykład sekwencyjnego DSL

Oto bardzo prosty szablon DSL automatyzacji, który wykorzystuje sekwencyjny przepływ składający się z elementu początkowego, warunku i działania:

import GoogleHomeSDK
import GoogleHomeTypes

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

Możesz to poprawić, dodając kolejne węzły.

Początkujący

Węzły początkowe określają początkowe okoliczności, które aktywują automatyzację. Na przykład zmiana stanu lub wartości. Automatyzacja musi mieć co najmniej 1 element początkowy, w przeciwnym razie nie przejdzie weryfikacji. Aby dodać do automatyzacji więcej niż jeden element początkowy, musisz użyć węzła select.

Starter na podstawie atrybutu cechy

Podczas deklarowania węzła początkowego opartego na atrybucie cechy podaj:

  • urządzenie
  • typ urządzenia, do którego należy cecha;
  • cecha
starter(
  thermostat,
  Matter.TemperatureSensorDeviceType.self,
  Matter.TemperatureMeasurementTrait.self
)

Parametr typu urządzenia jest wymagany, ponieważ umożliwia określenie, do którego typu urządzenia w ramach urządzenia odnosi się automatyzacja. Urządzenie może się np. składać z FanDeviceTypeHeatingCoolingUnitDeviceType, z których oba zawierają cechę OnOffTrait. Określenie typu urządzenia eliminuje niejasności co do tego, która część urządzenia wywołuje automatyzację.

Lista startowa na podstawie zdarzenia

Podczas deklarowania węzła początkowego opartego na zdarzeniu określ:

  • urządzenie
  • typ urządzenia, do którego należy cecha;
  • wydarzenie,
starter(
  doorbell,
  Google.GoogleDoorbellDeviceType.self,
  Google.DoorbellPressTrait.DoorbellPressedEvent
)

Wstępna wersja oparta na strukturze i zdarzeniu z parametrami

Niektóre zdarzenia mogą mieć parametry, więc te parametry też muszą być uwzględnione w wersji początkowej.

Na przykład ten inicjator używa TimeTraitScheduledEvent do aktywowania automatyzacji o 7:00:

typealias TimeTrait = Google.TimeTrait

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

Rozrusznik ręczny

Ręczny wyzwalacz to specjalny typ wyzwalacza, który umożliwia użytkownikowi ręczne uruchamianie automatyzacji.

Podczas deklarowania ręcznego startera:

  • Nie określaj cechy ani typu urządzenia.
  • Podaj element interfejsu, który wywołuje funkcję Automation.execute().

Jeśli umieścisz ręczny początek w select przepływie razem z innym początkiem, ręczny początek zastąpi ten drugi:

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

Pamiętaj, że wszystkie conditionwęzły następujące po ręcznym węźle początkowym zostaną sprawdzone i mogą zablokować wykonanie automatyzacji w zależności od conditionwyrażenia.

Oddzielanie ręcznego wyzwalacza od warunkowego

Jednym ze sposobów skonstruowania automatyzacji tak, aby węzły condition nie blokowały automatyzacji aktywowanej za pomocą ręcznego startera, jest umieszczenie innego startera w osobnym przepływie sekwencyjnym wraz z węzłem condition:

import GoogleHomeSDK
import GoogleHomeTypes

automation (
...
) {

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

}

Odwoływanie się do wartości atrybutu

Aby użyć w wyrażeniu wartości atrybutu, zastosuj tę składnię:

W przypadku stateReader:

typealias TimeTrait = Google.TimeTrait

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

W przypadku starter:

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

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

Węzły warunków i wyrażenia

Węzeł warunku reprezentuje punkt decyzyjny, który określa, czy automatyzacja będzie kontynuowana. Automatyzacja może mieć wiele węzłów condition. Jeśli wyrażenie dowolnego węzła condition przyjmie wartość false, wykonanie całej automatyzacji zostanie zakończone.

W węźle condition możesz łączyć wiele kryteriów warunku za pomocą różnych operatorów, o ile wyrażenie daje w wyniku pojedynczą wartość logiczną. Jeśli wynikowa wartość to true, warunek jest spełniony, a automatyzacja kontynuuje wykonywanie następnego węzła. Jeśli jest to false, automatyzacja przestaje działać w tym momencie.

Wyrażenia są tworzone podobnie jak wyrażenia w Swift i mogą zawierać wartości pierwotne, takie jak liczby, znaki, ciągi tekstowe i wartości logiczne, a także wartości wyliczeniowe. Grupowanie podwyrażeń za pomocą nawiasów pozwala kontrolować kolejność ich obliczania.

Oto przykład condition, który łączy wiele podwyrażeń w jedno wyrażenie:

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

Możesz odwoływać się do wartości cechy, do której dostęp uzyskano za pomocą elementu początkowego:

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

Innym sposobem odwoływania się do wartości atrybutów cech w węźle condition jest użycie węzła stateReader.

Aby to zrobić, najpierw przechwyć wartość atrybutu cechy w węźle stateReader. Funkcja stateReader przyjmuje jako argumenty structure i cechę:

typealias ActivatedCarbonFilterMonitoringTrait = Matter.ActivatedCarbonFilterMonitoringTrait

let filterMonitoringState = stateReader(structure, ActivatedCarbonFilterMonitoringTrait.self)

Następnie odwołaj się do stateReader w węźle condition:

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

Za pomocą operatorów porównanialogicznych w węźle condition można użyć wielu operatorów 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)
}

Czas trwania warunku

Oprócz wyrażenia logicznego w warunku możesz określić przedział czasu, w którym wyrażenie musi być prawdziwe, aby uruchomić automatyzację. Możesz na przykład zdefiniować warunek, który będzie aktywowany tylko wtedy, gdy światło jest włączone od 10 minut.

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

Może to być od 1 do 30 minut.

Węzły działań

W węźle działania odbywa się praca automatyzacji. W tym przykładzie działanie wywołuje polecenie AssistantBroadcastTraitbroadcast():

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