Przewodnik po DSL na Androida na potrzeby 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 na Androidzie.

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:

automation {
  sequential {
    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.

automation {
  sequential {
    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 jest 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 lub 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.
automation {
  sequential {
    val contactState = starter<_>(
      contactSensor,
      ContactSensorDevice,
      BooleanState,
    )
    val lightState = stateReader<_>(
      light,
      DimmableLightDevice,
      OnOff,
    )

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

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

    // Continue executing subsequent actions in the sequential flow
    action(structure) {
      command(AssistantBroadcast.broadcast("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.
automation {
  sequential {
    val doorLockEvent = starter<_>(
      doorLock,
      DoorLockDevice,
      LockOperationEvent,
    )
    val lightState = stateReader<_>(
      light,
      DimmableLightDevice,
      OnOff,
    )

    condition {
      expression =
        doorLockEvent.lockOperationType equals LockOperationTypeEnum.Unlock
    }

    ifThen(lightState.onOff equals false) {
      action(light, DimmableLightDevice) { command(OnOff.on()) }
    }.orElse {
      action(structure) {
        command(AssistantBroadcast.broadcast("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.
automation {
  sequential {
    val tempStarter = starter<_>(
      tempSensor,
      TemperatureSensorDevice,
      TemperatureMeasurement,
    )

    // If temperature is high (>= 28°C / 2800 mC), switch thermostat to Cool mode
    ifThen(tempStarter.measuredValue greaterThanOrEquals 2800) {
      action(thermostat, ThermostatDevice) {
        command(
          SimplifiedThermostat.setSystemMode(
            SimplifiedThermostatSystemModeEnum.Cool
          )
        )
      }
    }.elseIf(tempStarter.measuredValue lessThan 1800) {
      // If temperature is low (< 18°C / 1800 mC), switch to Heat mode
      action(thermostat, ThermostatDevice) {
        command(
          SimplifiedThermostat.setSystemMode(
            SimplifiedThermostatSystemModeEnum.Heat
          )
        )
      }
    }.orElse {
      // Otherwise, turn on the fan
      action(fan, FanDevice) {
        command(OnOff.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, delayFor 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 bloki action, stateReader, parallel, delayFor i zagnieżdżone bloki ifThen.

Opóźnienia

W automatyzacjach możesz wprowadzać przerwy za pomocą słowa kluczowego delayFor , które przyjmuje argument java.time.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 przełączyć światło 4 razy z 5-sekundową przerwą między każdym przełączeniem:

sequential {
  action(light, OnOffLightDevice) { command(OnOff.toggle()) }
  delayFor(Duration.ofSeconds(5))
  action(light, OnOffLightDevice) { command(OnOff.toggle()) }
  delayFor(Duration.ofSeconds(5))
  action(light, OnOffLightDevice) { command(OnOff.toggle()) }
  delayFor(Duration.ofSeconds(5))
  action(light, OnOffLightDevice) { command(OnOff.toggle()) }
}

Tłumienie reguły

Tłumienie 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 tłumienia reguły na 5 minut, to gdy starter zostanie wyzwolony, nie zostanie wyzwolony ponownie przez następne 5 minut. Zapobiega to szybkiemu i powtarzającemu się wyzwalaniu automatyzacji.

Aby zastosować tłumienie reguły w automatyzacji, użyj słowa kluczowego suppressFor z argumentem java.time.Duration określającym, jak długo należy czekać przed zareagowaniem na kolejne reguły. Czas tłumienia może wynosić od 5 sekund do 24 godzin.

automation {
  sequential {
    val starterNode = starter<_>(device, OccupancySensor, MotionDetection)
    suppressFor(Duration.ofMinutes(30))
    action(light, OnOffLightDevice) { command(OnOff.toggle()) }
}

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

Ograniczanie liczby uruchomień

Możesz ograniczyć liczbę uruchomień automatyzacji.

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

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

automation {
  // The automation can only be executed once.
  maxExecutionCount = 1
  // When the door lock state changes
  sequential {
    val doorLockEvent = starter<_>(doorLock, DoorLockDevice, LockOperationEvent)
    // if the door is unlocked
    condition() {
      expression = (doorLockEvent.lockOperationType equals LockOperationTypeEnum.Unlock)
    }
    // turn the light on
    action(light, DimmableLightDevice) { command(OnOff.on()) }
  }
}

Automatyzacja jest natychmiast usuwana po ostatnim wykonaniu i osiągnięciu wartości maxExecutionCount. Wpis historii automatyzacji pozostaje na karcie Google Home app (GHA) 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:

action(device, Fan) {
  update(FanControl) {
    setPercentSetting(50u)
    setRockSetting(FanControlCluster.RockBitmap.rockUpDown)
  }
}