Przewodnik po DSL na Androida

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

Cały język DSL automatyzacji znajduje się w jednym węźle automation. Węzeł automation stanowi granicę między zewnętrznym kontekstem języka Kotlin a osadzonym kontekstem DSL.

Przepływ sekwencyjny

Przepływ sekwencyjny to domyślny typ przepływu automatyzacji.

Przykład sekwencyjnego DSL

Oto bardzo prosty szablon DSL automatyzacji, który wykorzystuje sekwencyjny przepływ składający się z elementu początkowego, 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żesz to poprawić, dodając kolejne węzły.

Początkujący

Węzły początkowe określają początkowe okoliczności, które aktywują automatyzację. Na przykład zmiana stanu lub wartości. Automatyzacja musi mieć co najmniej 1 element początkowy, w przeciwnym razie nie przejdzie weryfikacji. Aby dodać do automatyzacji więcej niż jeden element początkowy, musisz użyć węzła select.

Starter na podstawie atrybutu cechy

Podczas deklarowania węzła początkowego opartego na atrybucie cechy podaj:

  • urządzenie
  • typ urządzenia, do którego należy cecha;
  • cecha
starter<_>(thermostat, TemperatureSensorDevice, TemperatureMeasurement)

Parametr typu urządzenia jest wymagany, ponieważ umożliwia określenie, do którego typu urządzenia w ramach urządzenia odnosi się automatyzacja. Urządzenie może się np. składać z FanDeviceHeatingCoolingUnitDevice, z których oba zawierają cechę OnOff. Określenie typu urządzenia eliminuje niejasności co do tego, która część urządzenia wywołuje automatyzację.

Lista startowa na podstawie zdarzenia

Podczas deklarowania węzła początkowego opartego na zdarzeniu określ:

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

Wstępna wersja oparta na strukturze i zdarzeniu z parametrami

Niektóre zdarzenia mogą mieć parametry, więc te parametry też muszą być uwzględnione w wersji początkowej.

Na przykład ten starter używa cechy Time ScheduledTimeEvent, aby aktywować automatyzację o 7:00:

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

Rozrusznik ręczny

Ręczny wyzwalacz to specjalny typ wyzwalacza, który umożliwia użytkownikowi ręczne uruchamianie automatyzacji.

Podczas deklarowania ręcznego startera:

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

Jeśli umieścisz ręczny początek w select przepływie razem z innym początkiem, ręczny początek zastąpi ten drugi:

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

Pamiętaj, że wszystkie conditionwęzły następujące po ręcznym węźle początkowym zostaną sprawdzone i mogą zablokować wykonanie automatyzacji w zależności od conditionwyrażenia.

Oddzielanie ręcznego wyzwalacza od warunkowego

Jednym ze sposobów skonstruowania automatyzacji tak, aby węzły condition nie blokowały automatyzacji aktywowanej za pomocą ręcznego startera, jest umieszczenie innego startera w osobnym przepływie sekwencyjnym wraz z węzłem condition:

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

Odwoływanie się do wartości atrybutu

Aby użyć w wyrażeniu wartości atrybutu, zastosuj tę 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 warunków i wyrażenia

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 daje w wyniku pojedynczą wartość logiczną. Jeśli wynikowa wartość to true, warunek jest spełniony, a 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 wyrażenia w języku Kotlin i mogą zawierać wartości pierwotne, takie jak liczby, znaki, ciągi tekstowe i wartości logiczne, a także wartości wyliczeniowe. Grupowanie podwyrażeń za pomocą nawiasów pozwala kontrolować kolejność ich obliczania.

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

Możesz odwoływać się do wartości cechy, do której dostęp uzyskano za pomocą elementu początkowego:

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

stateReader

Innym sposobem odwoływania 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. Funkcja stateReader przyjmuje jako argumenty structure i cechę:

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

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

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

Za pomocą operatorów porównanialogicznych w węźle condition można użyć wielu 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 uruchomić automatyzację. Możesz na przykład zdefiniować warunek, który będzie aktywowany tylko wtedy, gdy światło jest włączone od 10 minut.

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

Może to być od 1 do 30 minut.

Węzły działań

W węźle działania odbywa się praca automatyzacji. W tym przykładzie działanie wywołuje polecenie AssistantBroadcast cechy broadcast():

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

Importowanie wyciągów

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

Atrybuty cechy są importowane z obiektu Companion cechy:

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

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

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

Polecenia cechy są importowane z obiektu Companion cechy:

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