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 korzysta z sekwencyjnego przepływu składającego 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.

Starter

Węzły poleceń inicjujących 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ż 1 element wywołujący, musisz użyć węzła select.

Starter na podstawie atrybutu cechy

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

  • urządzenie
  • typ urządzenia, do którego należy cecha;
  • cechę
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. Na przykład urządzenie może składać się z FanDeviceTypeHeatingCoolingUnitDeviceType, z których oba zawierają cechę OnOffTrait. Określenie typu urządzenia eliminuje niejednoznaczność 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 to polecenie inicjujące używa TimeTraitScheduledEvent, aby aktywować automatyzację o 7:00:

typealias TimeTrait = Google.TimeTrait

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

Starter na podstawie pogody

W promptach możesz określać aktualne lub prognozowane warunki pogodowe, używając cechy Weather:

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

Zobacz Zasłoń rolety, jeśli prawdopodobnie będzie padać deszcz na stronie Przykłady automatyzacji.

Rozrusznik ręczny

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

Podczas deklarowania ręcznego warunku inicjującego:

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

Jeśli w przepływie select umieścisz ręczne polecenie wraz z innym poleceniem, ręczne polecenie zastąpi inne polecenie:

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

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

Oddzielanie ręcznego wyzwalacza od warunkowego

Jednym ze sposobów na skonstruowanie automatyzacji tak, aby węzły condition nie blokowały automatyzacji aktywowanej za pomocą ręcznego polecenia, jest umieszczenie innego polecenia 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 i wyrażenia warunku

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 jedną 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 w języku Swift i mogą zawierać wartości pierwotne, takie jak liczby, znaki, ciągi znaków 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łać 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 A 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ęzeł działania to miejsce, w którym 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")
}