Android DSL-Leitfaden

In der folgenden Anleitung erfahren Sie, wie verschiedene Automation DSL-Knoten zum Erstellen einer Automatisierung verwendet werden können.

Die gesamte Automatisierungs-DSL befindet sich in einem einzelnen automation-Knoten. Der Knoten automation bildet die Grenze zwischen dem äußeren Kotlin-Sprachkontext und dem eingebetteten DSL-Kontext.

Sequenzieller Ablauf

Der sequenzielle Ablauf ist der Standardtyp für Automatisierungsabläufe.

Beispiel für sequenzielle DSL

Hier ist eine sehr einfache Automation DSL-Vorlage, die einen sequenziellen Ablauf mit einem Starter, einer Bedingung und einer Aktion verwendet:


import com.google.home.automation.action
import com.google.home.automation.automation
import com.google.home.automation.condition
import com.google.home.automation.sequential
import com.google.home.automation.starter

...

automation {
  sequential {
    starter<_>(...)
    condition {...}
    action {...}
  }
}

Durch das Hinzufügen weiterer Knoten kann das Ergebnis noch weiter verfeinert werden.

Starter

Auslöserknoten definieren die anfänglichen Umstände, die eine Automatisierung aktivieren. Das kann beispielsweise eine Änderung des Status oder des Werts sein. Eine Automatisierung muss mindestens einen Starter haben, da sie sonst die Validierung nicht besteht. Wenn Sie einer Automatisierung mehr als einen Starter hinzufügen möchten, müssen Sie einen select-Knoten verwenden.

Starter basierend auf dem Attribut „Merkmal“

Wenn Sie einen Startknoten deklarieren, der auf einem Trait-Attribut basiert, geben Sie Folgendes an:

  • das Gerät
  • Der Gerätetyp, zu dem das Merkmal gehört
  • die Eigenschaft
starter<_>(thermostat, TemperatureSensorDevice, TemperatureMeasurement)

Der Parameter „deviceType“ ist erforderlich, da Sie damit angeben können, auf welchen Gerätetyp innerhalb eines Geräts sich die Automatisierung bezieht. Ein Gerät kann beispielsweise aus einem FanDevice und einem HeatingCoolingUnitDevice bestehen, die beide das Merkmal OnOff enthalten. Durch die Angabe des Gerätetyps wird eindeutig festgelegt, welcher Teil des Geräts die Automatisierung auslöst.

Starter basierend auf Ereignis

Wenn Sie einen Startknoten deklarieren, der auf einem Ereignis basiert, geben Sie Folgendes an:

  • das Gerät
  • Der Gerätetyp, zu dem das Merkmal gehört
  • das Ereignis
starter<_>(doorBell, GoogleDoorbellDevice, DoorbellPressed)

Starter auf Grundlage einer Struktur und eines Ereignisses mit Parametern

Einige Ereignisse können Parameter haben. Diese Parameter müssen daher auch im Starter enthalten sein.

Dieser Auslöser verwendet beispielsweise das ScheduledTimeEvent-Attribut des Time-Traits, um den automatisierten Ablauf um 7:00 Uhr zu aktivieren:

val earlyMorning = starter<_>(structure, Time.ScheduledTimeEvent) {
  parameter(Time.ScheduledTimeEvent.clockTime(
    LocalTime.of(7, 0, 0, 0)))
}

Manueller Starter

Ein manueller Auslöser ist ein spezieller Auslösertyp, mit dem der Nutzer die Automatisierung manuell ausführen kann.

Beim Deklarieren eines manuellen Starters:

  • Geben Sie keine Eigenschaft oder keinen Gerätetyp an.
  • Stellen Sie ein UI-Element bereit, das Automation.execute() aufruft.

Wenn Sie einen manuellen Auslöser in einem select-Ablauf zusammen mit einem anderen Auslöser platzieren, wird der andere Auslöser durch den manuellen Auslöser überschrieben:

select {
  manualStarter()
  starter<_>(thermostat, TemperatureSensorDevice, TemperatureMeasurement)
}

Alle condition-Knoten, die auf einen manuellen Starter folgen, werden ausgewertet und können die Ausführung der Automatisierung blockieren, je nach condition-Ausdruck.

Manuellen Starter von einer Bedingung trennen

Eine Möglichkeit, Ihre Automatisierung so zu strukturieren, dass condition-Knoten eine Automatisierung, die mit einem manuellen Auslöser aktiviert wurde, nicht blockieren, besteht darin, den anderen Auslöser zusammen mit seinem condition in einen separaten sequenziellen Ablauf zu stellen:

automation_graph {
  sequential {
    select {
      sequential {
        starter<_>(...)
        condition {...}
      }
      sequential {
        manualStarter()
      }
    }
    action {...}
  }
}

Auf den Wert eines Attributs verweisen

Wenn Sie den Wert eines Attributs in einem Ausdruck verwenden möchten, verwenden Sie die folgende Syntax.

Mit einem stateReader:

val time = stateReader<_>(structure, Structure, Time)
val currTime = time.currentTime

Mit einem starter:

val starterNode = starter<_>(device1, LaundryWasherDevice, OnOff)
condition() {
  expression = starterNode.onOff equals true
}

Bedingungsknoten und ‑ausdrücke

Ein Bedingungsknoten stellt einen Entscheidungspunkt dar, der bestimmt, ob die Automatisierung fortgesetzt wird. Eine Automatisierung kann mehrere condition-Knoten haben. Wenn der Ausdruck eines condition-Knotens als false ausgewertet wird, wird die Ausführung der gesamten Automatisierung beendet.

In einem condition-Knoten können Sie mehrere Bedingungskriterien mit verschiedenen Operatoren kombinieren, solange der Ausdruck zu einem einzelnen booleschen Wert ausgewertet wird. Wenn der resultierende Wert true ist, ist die Bedingung erfüllt und die Automatisierung wird mit dem nächsten Knoten fortgesetzt. Wenn der Wert false ist, wird die Automatisierung an dieser Stelle beendet.

Ausdrücke werden ähnlich wie in Kotlin gebildet und können primitive Werte wie Zahlen, Zeichen, Strings und boolesche Werte sowie Enum-Werte enthalten. Durch Gruppieren von Unterausdrücken mit Klammern können Sie die Reihenfolge steuern, in der sie ausgewertet werden.

Hier ein Beispiel für einen condition, der mehrere Unterausdrücke in einem einzigen Ausdruck kombiniert:

condition() {
  val expr1 = starterNode.lockState equals DlLockState.Unlocked
  val expr2 = stateReaderNode.lockState equals true

  val expr3 = occupancySensingDevice.occupied notEquals 0
  val expr4 = timeStateReaderNode
    .currentTime
    .between(
      timeStateReaderNode.sunsetTime,
      timeStateReaderNode.sunriseTime)
  expression = (expr1 and expr2) or (expr3 and expr4)
}

Sie können auf den Wert eines Merkmals verweisen, auf das über einen Starter zugegriffen wird:

val starterNode = starter<_>(device, OnOff)
condition() { expression = starterNode.onOff equals true }

stateReader

Eine weitere Möglichkeit, auf Attributwerte von Eigenschaften in einem condition-Knoten zu verweisen, ist ein stateReader-Knoten.

Erfassen Sie dazu zuerst den Attributwert des Merkmals in einem stateReader-Knoten. Für stateReader werden die structure und das Attribut als Argumente verwendet:

import com.google.home.automation.stateReader
...
val filterMonitoringState = stateReader<_>(structure, ActivatedCarbonFilterMonitoring)

Verweisen Sie dann im Knoten condition auf stateReader:

condition() {
  expression =
    filterMonitoringState.changeIndication
      .equals(ChangeIndicationEnum.Warning)
}

Mithilfe von Vergleichs- und logischen Operatoren können mehrere stateReaders in einem condition-Knoten verwendet werden:

val armState = stateReader<_>(doorLock, DoorLockDevice, ArmDisarm )
val doorLockState = stateReader<_>(doorLock, DoorLockDevice, DoorLock)
condition() {
  expression =
    (armState.armState equals true)
    and
    (doorLockState.lockState equals true)
}

Dauer der Bedingung

Zusätzlich zu einem booleschen Ausdruck in einer Bedingung können Sie einen Zeitraum angeben, in dem der Ausdruck „wahr“ sein muss, damit die Automatisierung ausgeführt wird. Sie können beispielsweise eine Bedingung definieren, die nur ausgelöst wird, wenn eine Lampe zehn Minuten lang eingeschaltet war.

  condition {
    expression(lightStateReader.onOff == true)
    forDuration(Duration.ofMinutes(10))
  }

Die Dauer kann zwischen einer und 30 Minuten liegen.

Aktionsknoten

Im Aktionsknoten wird die Arbeit der Automatisierung ausgeführt. In diesem Beispiel ruft die Aktion den Befehl broadcast() des Traits AssistantBroadcast auf:

action(device, SpeakerDevice) {
  command(AssistantBroadcast.broadcast("Intruder detected!"))
}

Importanweisungen

Beim Entwickeln von Automatisierungen ist es nicht immer offensichtlich, wie verschiedene Elemente der Home-APIs in Ihren Code importiert werden.

Die Attributwerte werden aus dem Companion-Objekt des Traits importiert:

import com.google.home.matter.standard.OnOff.Companion.onOff

Datenstrukturen, die durch ein Attribut definiert werden, werden aus der Attributklasse importiert, deren Name mit „-Trait“ endet:

import com.google.home.matter.standard.MediaPlaybackTrait.PlaybackStateEnum

Trait-Befehle werden aus dem Companion-Objekt des Traits importiert:

import com.google.home.matter.standard.Thermostat.Companion.setTemperatureSetpointHold