Przewodnik po DSL na Androida

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 Kotlin 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 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

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, 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 urządzenia FanDevice i urządzenia HeatingCoolingUnitDevice, z których oba zawierają cechę OnOff. 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, GoogleDoorbellDevice, DoorbellPressed)

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 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, 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 element ręcznego uruchamiania razem z innym elementem uruchamiania, element ręcznego uruchamiania zastąpi inny element:

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 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 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 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ę:

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żyć w węźle condition wielu elementów 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 się aktywować 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 AssistantBroadcasttrait'sbroadcast():

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