Przewodnik po DSL na iOS dotyczący złożonych automatyzacji

Język DSL automatyzacji umożliwia tworzenie bardziej złożonych automatyzacji niż te omówione w przewodniku po DSL – podstawowe automatyzacje w iOS.

Sekwencyjne z wieloma działaniami

Sekwencyjne z wieloma działaniami

Automatyzacja może wykonywać więcej niż jedną czynność. Na przykład zamiast pojedynczego węzła action możesz mieć kilka węzłów action, które są uruchamiane w kolejności sekwencyjnej:

import GoogleHomeSDK
import GoogleHomeTypes

automation (
...
) {

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

}

Sekwencyjne z wieloma działaniami równoległymi

Sekwencyjne z wieloma działaniami równoległymi

Jeśli umieścisz kilka węzłów action w węźle parallel, działania będą wykonywane równocześnie.

import GoogleHomeSDK
import GoogleHomeTypes

automation (
...
) {

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

}

Jeśli w węźle sequential znajdują się węzły action, które występują po węźle parallel, czekają one na wykonanie, aż wszystkie węzły w węźle parallel zostaną wykonane.

Wykonanie warunkowe

Domyślnie automatyzacja wykonuje węzły sekwencyjnie lub równolegle. Jeśli potrzebujesz logiki rozgałęziania warunkowego – wykonywania różnych działań lub ścieżek na podstawie warunków środowiska wykonawczego – użyj instrukcji if-then-else, które są tworzone za pomocą bloków DSL przepływu sterowania warunkowego:ifThen,elseIfiorElse.

Standardowy węzeł condition blokuje całą automatyzację (jeśli warunek ma wartość false, automatyzacja natychmiast się kończy), a bloki ifThen umożliwiają rozgałęzianie przepływu sterowania:

  • Węzeł condition: zatrzymuje wykonanie całej automatyzacji (lub bieżącej ścieżki wykonania), jeśli wyrażenie ma wartość false.
  • ifThen / elseIf / orElse: ocenia warunki w kolejności. Jeśli warunek ma wartość false, wykonanie przechodzi do następnej gałęzi elseIf, gałęzi rezerwowej orElse lub kontynuuje do kolejnych węzłów w automatyzacji, jeśli żaden warunek nie zostanie spełniony.

Wykonywanie działania na podstawie warunku

Blok ifThen ocenia wyrażenie warunkowe. Jeśli wyrażenie ma wartość true, wykonywane są działania lub węzły DSL zagnieżdżone w bloku. Jeśli ma wartość false, działania są pomijane, a automatyzacja przechodzi do kolejnych węzłów w przepływie sekwencyjnym.

Możesz użyć samodzielnego bloku ifThen, gdy chcesz wykonać działanie warunkowo bez blokowania ani kończenia kolejnych węzłów w automatyzacji:

// When a door opens, turn on the light only if it is off,
// and always broadcast an announcement.
typealias ContactSensorDevice = Matter.ContactSensorDeviceType
typealias BooleanStateTrait = Matter.BooleanStateTrait
typealias DimmableLightDevice = Matter.DimmableLightDeviceType
typealias OnOffTrait = Matter.OnOffTrait

automation {
  let contactState = starter(
    contactSensor,
    ContactSensorDevice.self,
    BooleanStateTrait.self
  )
  let lightState = stateReader(
    light,
    DimmableLightDevice.self,
    OnOffTrait.self
  )
  contactState
  lightState

  condition {
    // Door opened (contact sensor open)
    contactState.stateValue.equals(false)
  }

  // Conditionally turn on the light if it's off
  ifThen(lightState.onOff.equals(false)) {
    action(light, DimmableLightDevice.self) {
      OnOffTrait.on()
    }
  }

  // Continue executing subsequent actions in the sequential flow
  action(structure) {
    Google.AssistantBroadcastTrait.broadcast(msg: "The door was opened.")
  }
}

Wykonywanie różnych działań na podstawie warunku

Aby wykonać jeden zestaw działań, gdy warunek jest spełniony, i alternatywny zestaw działań, gdy nie jest spełniony, połącz opcjonalny blok .orElse { ... } z blokiem ifThen(...) { ... }:

// When the door is unlocked, turn on the entryway light if it is off;
// otherwise, broadcast a welcome message.
typealias DoorLockDevice = Matter.DoorLockDeviceType
typealias DoorLockTrait = Matter.DoorLockTrait
typealias DimmableLightDevice = Matter.DimmableLightDeviceType
typealias OnOffTrait = Matter.OnOffTrait

automation {
  let doorLockEvent = starter(
    doorLock,
    DoorLockDevice.self,
    DoorLockTrait.LockOperationEvent.self
  )
  let lightState = stateReader(
    light,
    DimmableLightDevice.self,
    OnOffTrait.self
  )
  doorLockEvent
  lightState

  condition {
    doorLockEvent.lockOperationType.equals(.unlock)
  }

  ifThen(lightState.onOff.equals(false)) {
    action(light, DimmableLightDevice.self) {
      OnOffTrait.on()
    }
  }.orElse {
    action(structure) {
      Google.AssistantBroadcastTrait.broadcast(msg: "Welcome home!")
    }
  }
}

Łączenie wielu warunków w sekwencji

Możesz połączyć jeden lub więcej opcjonalnych bloków .elseIf(...) { ... }, aby ocenić wiele warunków w sekwencji. Wykonana zostanie pierwsza gałąź, której warunek ma wartość true, a wszystkie pozostałe gałęzie zostaną pominięte. Jeśli żaden z warunków nie ma wartości true, wykonywany jest opcjonalny blok .orElse { ... } (jeśli jest dostępny):

// Adjust climate controls based on room temperature changes.
typealias TemperatureSensorDeviceType = Matter.TemperatureSensorDeviceType
typealias TemperatureMeasurementTrait = Matter.TemperatureMeasurementTrait
typealias ThermostatDeviceType = Matter.ThermostatDeviceType
typealias SimplifiedThermostatTrait = Google.SimplifiedThermostatTrait
typealias FanDeviceType = Matter.FanDeviceType
typealias OnOffTrait = Matter.OnOffTrait

automation {
  let tempStarter = starter(
    tempSensor,
    TemperatureSensorDeviceType.self,
    TemperatureMeasurementTrait.self
  )
  tempStarter

  // If temperature is high (>= 28°C / 2800 mC), switch thermostat to Cool mode
  ifThen(tempStarter.measuredValue.greaterThanOrEquals(2800)) {
    action(thermostat, ThermostatDeviceType.self) {
      SimplifiedThermostatTrait.setSystemMode(systemMode: .cool)
    }
  }.elseIf(tempStarter.measuredValue.lessThan(1800)) {
    // If temperature is low (< 18°C / 1800 mC), switch to Heat mode
    action(thermostat, ThermostatDeviceType.self) {
      SimplifiedThermostatTrait.setSystemMode(systemMode: .heat)
    }
  }.orElse {
    // Otherwise, turn on the fan
    action(fan, FanDeviceType.self) {
      OnOffTrait.on()
    }
  }
}

Zagnieżdżone przepływy warunkowe

Bloki warunkowe można zagnieżdżać w innych blokach ifThen, elseIf lub orElse, a także łączyć z węzłami parallel, delay(for:) i stateReader.

Bloki ifThen, elseIf i orElse wykonują swoją zawartość jako przepływy sekwencyjne. W każdej gałęzi możesz umieścić dowolne węzły sekwencyjne, w tym action, stateReader, parallel, delay(for:) i zagnieżdżone bloki ifThen.

Opóźnienia

Możesz wprowadzać przerwy w automatyzacjach za pomocą metody delay(for:), która przyjmuje argument Duration określający, jak długo ma trwać wstrzymanie przed kontynuowaniem wykonania. Czas wstrzymania może wynosić od 5 sekund do 24 godzin.

Aby na przykład 4 razy przełączyć światło z 5-sekundową przerwą między każdym przełączeniem:

typealias OnOffLightDevice = Matter.OnOffLightDeviceType
typealias OnOffTrait = Matter.OnOffTrait

sequential {
  action(light, OnOffLightDevice.self) { OnOffTrait.toggle() }
  delay(for:.seconds(5))
  action(light, OnOffLightDevice.self) { OnOffTrait.toggle() }
  delay(for:.seconds(5))
  action(light, OnOffLightDevice.self) { OnOffTrait.toggle() }
  delay(for:.seconds(5))
  action(light, OnOffLightDevice.self) { OnOffTrait.toggle() }
}

Blokowanie reguły

Blokowanie reguły to funkcja, która umożliwia automatyzacji ignorowanie starter przez określony czas po początkowym zdarzeniu wyzwalającym. Jeśli na przykład automatyzacja ma starter, który jest wyzwalany przez wykrycie ruchu, a Ty określisz czas blokowania reguły na 5 minut, to gdy starter zostanie wyzwolony, nie zostanie wyzwolony ponownie przez następne 5 minut. Zapobiega to szybkiemu i wielokrotnemu wyzwalaniu automatyzacji.

Aby zastosować blokowanie reguły w automatyzacji, użyj słowa kluczowego suppress(for:) z argumentem Duration określającym, jak długo należy czekać przed zareagowaniem na kolejne reguły. Czas blokowania może wynosić od 5 sekund do 24 godzin.

typealias OccupancySensorDevice = Matter.OccupancySensorDeviceType
typealias OnOffLightDevice = Matter.OnOffLightDeviceType
typealias MotionDetectionTrait = Google.MotionDetectionTrait
typealias OnOffTrait = Matter.OnOffTrait

automation {
  let starterNode = starter(device, OccupancySensorDevice.self, MotionDetectionTrait.self)
  starterNode
  suppress(for: .seconds(30 * 60)) // 30 minutes
  action(light, OnOffLightDevice.self) { OnOffTrait.toggle() }
}

Pamiętaj, że blokowanie aktywatora wpływa na wszystkie starters w automatyzacji, które poprzedzają Suppression.

Ograniczanie liczby uruchomień

Możesz ograniczyć liczbę uruchomień automatyzacji.

Możesz na przykład skonfigurować automatyzację jednorazową, która uruchamia odkurzacz, gdy nie ma Cię w domu.

Aby to zrobić, ustaw pole metadanych maxExecutionCount automatyzacji. Poniższy przykład to automatyzacja, która może być wykonana tylko raz:

import GoogleHomeSDK
import GoogleHomeTypes

typealias RoboticVacuumCleanerDevice = Matter.RoboticVacuumCleanerDeviceType
typealias RvcRunModeTrait = Matter.RvcRunModeTrait
typealias AreaPresenceStateTrait = Google.AreaPresenceStateTrait

let draftAutomation = automation(
  name: "Vacuum home away",
  description: "Run the vacuum once when everyone is away.",
  maxExecutionCount: 1
) {
  let homeAwayState = starter(structure, AreaPresenceStateTrait.self)
  homeAwayState

  condition {
    homeAwayState.presenceState.equals(.presenceStateVacant)
  }

  action(vacuum, RoboticVacuumCleanerDevice.self) {
    RvcRunModeTrait.changeToMode(newMode: 1)
  }
}

Automatyzacja jest natychmiast usuwana po ostatnim wykonaniu i osiągnięciu wartości maxExecutionCount. Wpis w historii automatyzacji pozostaje w Google Home app (GHA) na karcie Aktywność, w tym automation_id.

Ustawianie atrybutów cechy w działaniu

Aby ustawić wartość atrybutu cechy:

  1. Utwórz węzeł update w węźle action, w tym odpowiednią cechę jako argument węzła update:
    action(deviceReference, deviceType) {
      update(trait) {
    
      }
    }
    
  2. W węźle update dla każdego atrybutu, który ma zostać zmodyfikowany, użyj funkcji mutatora i przekaż jej nową wartość. Aby utworzyć nazwę funkcji mutatora:
    1. Zacznij nazwę atrybutu od wielkiej litery.
    2. Dodaj przed nią słowo set.
    Aby na przykład zaktualizować atrybut o nazwie defaultMoveRate, użyj funkcji mutatora o nazwie setDefaultMoveRate.

Pamiętaj, że węzeł update może mieć wiele funkcji mutatora. Oto przykład, w którym aktualizowane są 2 atrybuty:

typealias FanDeviceType = Matter.FanDeviceType
typealias FanControlTrait = Matter.FanControlTrait

action(fan, FanDeviceType.self) {
  update(FanControlTrait.self) {
    $0.setFanMode(.on)
  }
}