Przewodnik po DSL – podstawowe automatyzacje

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

Cały kod DSL automatyzacji jest umieszczony w pojedynczym węźle automation. automation stanowi granicę między zewnętrznym kontekstem języka Kotlin a osadzonym kontekstem DSL.

Sekwencyjny przepływ

Sekwencyjny to domyślny typ procedury 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 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 {...}
  }
}

Można 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 inicjujący, musisz użyć węzła wyboru.

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;
  • cecha
starter<_>(thermostat, TemperatureSensorDevice, TemperatureMeasurement)

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 FanDeviceHeatingCoolingUnitDevice, z których oba zawierają cechę OnOff. Określając typ urządzenia, usuwasz niejasności dotyczące tego, która część urządzenia uruchamia automatyzację.

Starter na podstawie zdarzenia

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

  • urządzenie
  • typ urządzenia, do którego należy cecha;
  • wydarzenie
starter<_>(doorBell, GoogleDoorbellDevice, DoorbellPressed)

Szablon 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 ten starter używa atrybutu Time ScheduledTimeEvent, aby aktywować automatyzację o godzinie 7:00 rano:

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

Ręczne uruchamianie

Rozpoczęcie ręczne to specjalny typ inicjatora, 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 Automation.execute().

Gdy w sekwencji select umieścisz element inicjujący ręczny razem z innym elementem inicjującym, element inicjujący ręczny zastąpi inny element inicjujący:

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

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:

automation_graph {
  sequential {
    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:

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

W przypadku starter:

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

Węzły i wyrażenia warunków

Węzeł warunku reprezentuje punkt podejmowania decyzji, który decyduje, czy automatyzacja będzie 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 zwraca 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 działać w tym momencie.

Wyrażenia są tworzone podobnie jak w Kotlinie 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() {
  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)
}

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

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

stateReader

Innym sposobem na odniesienie 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 kodzie stateReader. Funkcja stateReader przyjmuje jako argumenty structure i cechę:

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

Następnie odwołuj się do elementu stateReader w węźle condition:

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

Za pomocą operatorów porównawczych i operatorów logicznych możesz używać w węźle condition wielu funkcji stateReaders:

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

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 działać tylko wtedy, gdy światło było włączone przez 10 minut.

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

Czas trwania może wynosić od 1 do 30 minut.

Węzły akcji

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

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

Importowanie wyciągów

Podczas tworzenia automatyzacji nie zawsze wiadomo, jak zaimportować do kodu różne elementy interfejsów API Home.

Atrybuty cech są importowane z obiektu Companion danej cechy:

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

Struktury danych zdefiniowane przez cechę są importowane z klasy cech, której nazwa kończy się na „-Trait”:

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

Polecenia cech są importowane z obiektu Companion danej cechy:

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