Przewodnik DSL na iOS

Z tego przewodnika dowiesz się, jak używać różnych węzłów Automation DSL do tworzenia automatyzacji.

Cały kod Automation DSL 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 podstawowy szablon Automation DSL, który korzysta z przepływu sekwencyjnego składającego się z polecenia inicjującego, warunku i działania:

import GoogleHomeSDK
import GoogleHomeTypes

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

Możesz go udoskonalić, dodając dodatkowe węzły.

Polecenie inicjujące

Węzły polecenia inicjującego określają początkowe okoliczności, które aktywują automatyzację. Może to być np. zmiana stanu lub wartości. Automatyzacja musi mieć co najmniej 1 polecenie inicjujące, w przeciwnym razie nie przejdzie weryfikacji. Aby dodać do automatyzacji więcej niż 1 polecenie inicjujące, musisz użyć węzła select.

Polecenie inicjujące na podstawie atrybutu cechy

Deklarując węzeł polecenia inicjującego, który jest oparty 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ż pozwala określić, do którego typu urządzenia w urządzeniu odnosi się automatyzacja. Urządzenie może się np. składać z FanDeviceType i HeatingCoolingUnitDeviceType, 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ę.

Polecenie inicjujące na podstawie zdarzenia

Deklarując węzeł polecenia inicjującego, który jest oparty na zdarzeniu, określ:

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

Polecenie inicjujące na podstawie struktury i zdarzenia z parametrami

Niektóre zdarzenia mogą mieć parametry, dlatego należy je uwzględnić w poleceniu inicjującym.

Na przykład to polecenie inicjujące używa TimeTrait's ScheduledEvent 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))
}

Polecenie inicjujące na podstawie pogody

W poleceniu inicjującym możesz określić aktualne lub prognozowane warunki pogodowe za pomocą cechy Weather:

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

Na stronie Przykłady automatyzacji znajdziesz przykład Close the blinds if it is likely to rain (Zamknij rolety, jeśli prawdopodobnie będzie padać).

Ręczne polecenie inicjujące

Ręczne polecenie inicjujące to specjalny typ polecenia inicjującego, który umożliwia użytkownikowi ręczne uruchomienie automatyzacji.

Deklarując ręczne polecenie inicjujące:

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

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

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

Pamiętaj, że wszystkie węzły condition znajdujące się za ręcznym poleceniem inicjującym 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 ręcznym poleceniem inicjującym, jest umieszczenie innego polecenia inicjującego w osobnym przepływie sekwencyjnym wraz z jego condition:

import GoogleHomeSDK
import GoogleHomeTypes

automation (
...
) {

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

}

Odwoływanie się do wartości atrybutu

Aby użyć wartości atrybutu w wyrażeniu, użyj tej składni.

Za pomocą stateReader:

typealias TimeTrait = Google.TimeTrait

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

Za pomocą 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 warunków

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 przyjmuje pojedynczą wartość logiczną. Jeśli wartość wynikowa to true, warunek jest spełniony, a automatyzacja kontynuuje wykonywanie następnego węzła. Jeśli wartość to false, automatyzacja zatrzymuje się w tym momencie.

Wyrażenia są tworzone podobnie jak wyrażenia 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 wyrażeń podrzędnych za pomocą nawiasów pozwala kontrolować kolejność ich oceniania.

Oto przykład condition, który łączy wiele wyrażeń podrzędnych 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ą polecenia inicjującego:

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 na odwoływanie 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. 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)
}

W węźle condition można używać wielu stateReaders za pomocą operatorów porównania i logicznych:

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 można było uruchomić automatyzację. Możesz np. zdefiniować warunek, który uruchamia się tylko wtedy, gdy światło jest włączone przez 10 minut.

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

Czas trwania może wynosić 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 AssistantBroadcastTrait's broadcast() polecenie:

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