Utilizza la seguente guida per capire come i vari nodi DSL di automazione possono essere utilizzati per creare un'automazione.
Tutto il DSL di automazione viene inserito in un singolo nodo automation
. Il nodo
automation
forma il confine tra il contesto del linguaggio Kotlin esterno
e il contesto DSL incorporato.
Flusso sequenziale
Il flusso sequenziale è il tipo predefinito di flusso di automazione.
Di seguito è riportato un modello DSL di automazione molto semplice che utilizza un flusso sequenziale costituito da un comando iniziale, una condizione e un'azione:
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 {...}
}
}
Può essere perfezionato aggiungendo nodi aggiuntivi.
Starter
I nodi di comando iniziale definiscono le circostanze iniziali che attivano un'automazione. Ad esempio, una modifica dello stato o del valore. Un'automazione deve avere almeno un
trigger, altrimenti la convalida non andrà a buon fine. Per aggiungere più di un
trigger a un'automazione, devi utilizzare un nodo select
.
Starter basato sull'attributo caratteristica
Quando dichiari un nodo iniziale basato su un attributo di tratto, specifica:
- il dispositivo
- Il tipo di dispositivo a cui appartiene il tratto
- il tratto
starter<_>(thermostat, TemperatureSensorDevice, TemperatureMeasurement)
Il parametro del tipo di dispositivo è obbligatorio perché ti consente di specificare a quale tipo di dispositivo all'interno di un dispositivo si rivolge l'automazione. Ad esempio, un dispositivo potrebbe essere
composto da un
FanDevice
e da un
HeatingCoolingUnitDevice
,
entrambi contenenti la
caratteristica
OnOff
. Se specifichi il tipo di dispositivo, non ci sono ambiguità su quale parte del dispositivo attiva l'automazione.
Starter basato sull'evento
Quando dichiari un nodo iniziale basato su un evento, specifica:
- il dispositivo
- Il tipo di dispositivo a cui appartiene il tratto
- l'evento
starter<_>(doorBell, GoogleDoorbellDevice, DoorbellPressed)
Starter basato su una struttura e un evento, con parametri
Alcuni eventi possono avere parametri, quindi anche questi devono essere inclusi nello starter.
Ad esempio, questo comando iniziale utilizza l'attributo
Time
ScheduledTimeEvent
per attivare l'automazione alle 7:00:
val earlyMorning = starter<_>(structure, Time.ScheduledTimeEvent) {
parameter(Time.ScheduledTimeEvent.clockTime(
LocalTime.of(7, 0, 0, 0)))
}
Avviamento manuale
Un comando iniziale manuale è un tipo speciale di comando iniziale che consente all'utente di eseguire manualmente l'automazione.
Quando dichiari un comando iniziale manuale:
- Non specificare una caratteristica o un tipo di dispositivo.
- Fornisci un elemento UI che chiami
Automation.execute()
.
Quando inserisci un comando iniziale manuale in un flusso select
insieme a un altro comando iniziale, il
comando iniziale manuale sostituisce l'altro:
select {
manualStarter()
starter<_>(thermostat, TemperatureSensorDevice, TemperatureMeasurement)
}
Tieni presente che tutti i nodi condition
che seguono un nodo iniziale manuale verranno valutati
e potrebbero bloccare l'esecuzione dell'automazione, a seconda dell'espressione condition
.
Un modo per strutturare l'automazione in modo che i nodi condition
non blocchino un'automazione attivata con un comando iniziale manuale è inserire l'altro comando iniziale in un flusso sequenziale separato insieme al relativo condition
:
automation_graph {
sequential {
select {
sequential {
starter<_>(...)
condition {...}
}
sequential {
manualStarter()
}
}
action {...}
}
}
Fare riferimento al valore di un attributo
Per utilizzare il valore di un attributo in un'espressione, utilizza la seguente sintassi.
Con un stateReader
:
val time = stateReader<_>(structure, Structure, Time)
val currTime = time.currentTime
Con un starter
:
val starterNode = starter<_>(device1, LaundryWasherDevice, OnOff)
condition() {
expression = starterNode.onOff equals true
}
Nodi di condizione ed espressioni
Un nodo di condizione rappresenta un punto decisionale che determina se l'automazione continua o meno. Un'automazione può avere più nodi condition
.
Se l'espressione di un nodo condition
restituisce false
, l'esecuzione dell'intera automazione termina.
All'interno di un nodo condition
, puoi combinare più criteri di condizione utilizzando
vari operatori, purché l'espressione
restituisca un singolo
valore booleano. Se il valore risultante è true
, la condizione è soddisfatta e l'automazione continua l'esecuzione del nodo successivo. Se è false
, l'automazione
interrompe l'esecuzione a quel punto.
Le espressioni sono formate in modo simile a quelle in Kotlin e possono contenere valori primitivi come numeri, caratteri, stringhe e valori booleani, nonché valori Enum. Il raggruppamento delle sottoespressioni con le parentesi consente di controllare l'ordine in cui vengono valutate.
Ecco un esempio di condition
che combina più sottoespressioni in una
singola espressione:
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)
}
Puoi fare riferimento al valore di un tratto a cui si accede tramite un punto di partenza:
val starterNode = starter<_>(device, OnOff)
condition() { expression = starterNode.onOff equals true }
stateReader
L'altro modo per fare riferimento ai valori degli attributi delle caratteristiche in un nodo condition
è con
un nodo stateReader
.
Per farlo, acquisisci prima il valore dell'attributo caratteristica in un nodo stateReader
. Un
stateReader
accetta structure
e il tratto come argomenti:
import com.google.home.automation.stateReader
...
val filterMonitoringState = stateReader<_>(structure, ActivatedCarbonFilterMonitoring)
Quindi, fai riferimento a stateReader
nel nodo condition
:
condition() {
expression =
filterMonitoringState.changeIndication
.equals(ChangeIndicationEnum.Warning)
}
Utilizzando gli operatori di confronto e
logici, è possibile utilizzare più
stateReaders
in un nodo condition
:
val armState = stateReader<_>(doorLock, DoorLockDevice, ArmDisarm )
val doorLockState = stateReader<_>(doorLock, DoorLockDevice, DoorLock)
condition() {
expression =
(armState.armState equals true)
and
(doorLockState.lockState equals true)
}
Durata della condizione
Oltre a un'espressione booleana in una condizione, puoi specificare un periodo di tempo durante il quale l'espressione deve essere vera per eseguire l'automazione. Ad esempio, puoi definire una condizione che si attiva solo se una luce è accesa da dieci minuti.
condition {
expression(lightStateReader.onOff == true)
forDuration(Duration.ofMinutes(10))
}
La durata può variare da 1 a 30 minuti.
Nodi di azione
Il nodo Azione è il punto in cui viene eseguito il lavoro dell'automazione.
In questo esempio, l'azione richiama il comando
AssistantBroadcast
della caratteristica
broadcast()
:
action(device, SpeakerDevice) {
command(AssistantBroadcast.broadcast("Intruder detected!"))
}
Importare estratti conto
Quando sviluppi automazioni, non è sempre ovvio come importare vari elementi delle API Home nel tuo codice.
Gli attributi del tratto vengono importati dall'oggetto Companion
del tratto:
import com.google.home.matter.standard.OnOff.Companion.onOff
Le strutture di dati definite da un tratto vengono importate dalla classe di tratti il cui nome termina con "-Trait":
import com.google.home.matter.standard.MediaPlaybackTrait.PlaybackStateEnum
I comandi delle caratteristiche vengono importati dall'oggetto Companion
della caratteristica:
import com.google.home.matter.standard.Thermostat.Companion.setTemperatureSetpointHold