Przewodnik DSL na iOS

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

Cała automatyzacja DSL jest umieszczana w pojedynczym węźle automation. automation stanowi granicę między zewnętrznym kontekstem języka Swift a osadzonym kontekstem DSL.

Sekwencyjny

Sekwencyjny to domyślny typ przepływu automatyzacji.

Przykład sekwencyjnego DSL

Oto bardzo prosty szablon automatyzacji DSL, który używa sekwencyjnego przepływu, składającego się z elementu startowego, warunku i działania:

import GoogleHomeSDK
import GoogleHomeTypes

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

Możesz to doprecyzować, dodając dodatkowe węzły.

Początkujący

Początkowe węzły definiują początkowe okoliczności, które aktywują automatyzację. na przykład zmiana stanu lub wartości. Automatyzacja musi mieć co najmniej 1 element inicjujący, w przeciwnym razie nie przejdzie walidacji. Aby dodać do automatyzacji więcej niż 1 element startowy, musisz użyć węzła select.

Starter na podstawie atrybutu cech

Podczas deklarowania węzła startowego opartego na atrybucie cechy podaj:

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

Parametr device type jest wymagany, ponieważ pozwala określić, który typ urządzenia ma być obsługiwany przez automatyzację. Na przykład urządzenie może składać się z urządzenia FanDeviceType i urządzenia HeatingCoolingUnitDeviceType, z których każde zawiera cechę OnOffTrait. Określając typ urządzenia, usuwasz niejednoznaczność dotyczącą tego, która część urządzenia uruchamia automatyzację.

Starter na podstawie zdarzenia

Podczas deklarowania węzła startowego 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
)

Starter oparty na strukturze i zdarzeniu z parametrami

Niektóre zdarzenia mogą mieć parametry, więc te parametry muszą też zostać uwzględnione w starterze.

Na przykład to starter używa ScheduledEventTimeTrait, aby aktywować automatyzację o 7:00 rano:

typealias TimeTrait = Google.TimeTrait

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

Ręczne uruchamianie

Rozpoczęcie ręczne to specjalny typ, który umożliwia użytkownikowi ręczne uruchamianie automatyzacji.

Podczas deklarowania ręcznego uruchamiania:

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

Gdy w sekwencji select umieścisz ręczny początek razem z innym początkiem, ręczny początek zastąpi inny początek:

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

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

Oddzielanie ręcznego elementu startowego od elementu warunkowego

Jednym ze sposobów ustrukturyzowania automatyzacji tak, aby węzły condition nie blokowały automatyzacji aktywowanej za pomocą ręcznego startera, jest umieszczenie drugiego startera w osobnym sekwencyjnym przepływie 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.

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 warunków

Węzeł warunku reprezentuje punkt podejmowania decyzji, który decyduje, czy automatyzacja ma być kontynuowana. Automatyzacja może mieć wiele węzłów condition. Jeśli wyrażenie dowolnego węzła condition ma wartość false, kończy się wykonywanie całej automatyzacji.

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

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

Oto przykład condition, który łączy wiele podwyrażeń w jeden 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)
}

Wartość atrybutu możesz pobrać za pomocą startera:

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 odniesienie się do wartości atrybutów cech w węźle condition jest użycie węzła stateReader.

Aby to zrobić, najpierw uchwyć 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łuj się do elementu stateReader w węźle condition:

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

Za pomocą operatorów porównywania i operatorów logicznych w węźle condition można użyć wielu elementó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 automatyzacja mogła się wykonać. Możesz na przykład zdefiniować warunek, który będzie się aktywować tylko wtedy, gdy światło było 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łania

W węźle działania odbywa się automatyzacja. W tym przykładzie działanie wywołuje polecenie AssistantBroadcastTrait: broadcast()

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