Concetti DSL

Questa è una panoramica dei concetti fondamentali dell'Automation DSL.

Componenti di automazione

Un'automazione è costituita dai seguenti componenti di base, in genere valutati in questo ordine:

  1. Comando iniziale: definisce le condizioni iniziali che attivano l'automazione, come la modifica di un tratto. Un'automazione deve avere un comando iniziale.
  2. Condizione: eventuali vincoli aggiuntivi da valutare dopo l'attivazione di un'automazione. L'espressione in una condizione deve restituire true perché le azioni di un'automazione possano procedere.
  3. Azione: comandi o aggiornamenti dello stato che vengono eseguiti quando tutte le condizioni sono soddisfatte.

Ad esempio, potresti avere un'automazione che attenua le luci di una stanza quando la TV è accesa tra il tramonto e l'alba. In questo esempio:

  1. Starter: la TV è stata accesa, ovvero si è verificata una variazione di stato in un attributo della TV.
  2. Condizione: viene valutato l'ora corrente della casa in cui si trova la TV.
  3. Azione: le luci nella stessa stanza della TV vengono attenuate.

L'automazione viene attivata quando la TV in camera viene accesa, ma viene eseguita solo se è soddisfatta la condizione "l'ora è compresa tra il tramonto e l'alba".

Oltre alla struttura di base, le automazioni nelle API Home contengono anche metadati, come name e description, che possono essere utilizzati per identificarli per sviluppatori e utenti.

Nodi

Nelle API Home, la struttura logica di un'automazione è costituita da nodes. I nodi sono unità astratte riutilizzabili che rappresentano i comportamenti delle entità o i flussi di esecuzione. Ogni nodo può avere variabili di input, nonché variabili di output che possono essere utilizzate da altri nodi.

Tabella: tipi di nodi di automazione
Nodo Tipo di nodo Implementazione di Kotlin Descrizione
Comando iniziale Comportamentale StarterNodeDsl Avvia un'automazione quando cambia lo stato di un tratto (qualsiasi attributo).
StateReader Comportamentale StateReaderNodeDsl Legge un attributo tratto e ti consente di acquisire il relativo valore per utilizzarlo nei nodi condizione.
Azione Comportamentale ActionNodeDsl Richiama i comandi del tratto.
Sequenziale Flusso di esecuzione SequentialFlow Esegue i nodi di azioni nidificati in sequenza. Questo è il comportamento di esecuzione predefinito.
Parallelo Flusso di esecuzione ParallelFlow Esegue i nodi di azioni nidificati in parallelo.
Condizione Flusso di esecuzione ConditionNodeDsl Modifica in modo condizionale il flusso di esecuzione in base alle valutazioni delle espressioni logiche. Le condizioni possono essere associate a un comando iniziale (condizioni specifiche per i comandi iniziali) o essere globali (si applicano a tutti i comandi iniziali).
Seleziona Flusso di esecuzione SelectFlow Consente di attivare un'automazione con più di un comando iniziale.
Espressione Valore Expression Può essere il valore dell'attributo di un tratto, una costante o un valore letterale e deve restituire un elenco, un numero, un valore booleano o una stringa.

Nodi di comportamento

Nodi come comandi iniziali e azioni sono nodi di comportamento. I comandi iniziali attivano un'automazione in base alle modifiche degli attributi del dispositivo. Le azioni inviano comandi ai dispositivi o aggiornano gli attributi.

I nodi di comportamento sono in genere collegati alle caratteristiche del dispositivo e allo stato della caratteristica di output per essere utilizzati come input in altri nodi.

Nodi del flusso di esecuzione

Alcuni nodi rappresentano flussi di esecuzione, ad esempio sequenziali e paralleli. Ciascuno di questi nodi contiene i nodi di comportamento che definiscono l'automazione.

Ad esempio, un flusso sequenziale può contenere nodi che vengono eseguiti in ordine sequenziale. In genere, si tratta di comandi iniziali, condizioni e azioni.

Flussi di esecuzione sequenziali
Figura 1: flusso di automazione sequenziale

Un flusso parallelo può avere più nodi di azioni in esecuzione contemporaneamente, ad esempio l'accensione di più luci contemporaneamente. I nodi che seguono un flusso parallelo non verranno eseguiti fino al termine di tutti i rami del flusso parallelo.

Flussi di esecuzione parallela
Figura 2: flusso di automazione parallela

Un altro tipo di flusso di esecuzione è il flusso condizionale, che può modificare il flusso di esecuzione in base alla valutazione di un'espressione.

Ad esempio, potresti avere un'automazione che esegue un'azione in base al fatto che sia notte. Un nodo condizione controlla l'ora del giorno, quindi segue il percorso di esecuzione appropriato in base a questa valutazione.

Flusso delle condizioni
Figura 3: Flusso delle condizioni

Un flusso di selezione è utile quando vuoi avere più di un comando iniziale che può attivare l'automazione. Quando racchiudi due o più comandi iniziali in un flusso select, uno qualsiasi di questi può attivare l'automazione.

Ad esempio, puoi scrivere un'automazione che abbassa le persiane al tramonto, se la temperatura supera una determinata soglia o se la luminosità supera una soglia. Tre comandi iniziali separati gestiscono ciascuno di questi scenari e tutti e tre vengono inseriti in un flusso select.

Seleziona flusso
Figura 4: seleziona flusso

Flussi nidificati

Nelle automazioni complesse, i nodi del flusso di esecuzione possono anche essere nidificati. Ad esempio, puoi avere un flusso sequenziale che esegue un flusso parallelo.

Flussi di esecuzione nidificati
Figura 5: Flussi di esecuzione nidificati

I nodi DSL possono essere nidificati e combinati in vari modi per soddisfare le tue esigenze specifiche, in base ai vincoli descritti nella tabella seguente. La colonna Builder contiene link alla documentazione del costruttore sicuro di Kotlin, che descrive in dettaglio cosa è consentito utilizzare in ogni tipo di nodo.

Tabella: come combinare i nodi
Nodo Builder Può contenere i seguenti tipi di nodi e dati Deve rientrare in uno dei seguenti tipi di nodi
Comando iniziale AutomationBuilder Espressione Seleziona, Sequenziale
ManualStarter AutomationBuilder Seleziona, Sequenziale
StateReader AutomationBuilder Espressione (in genere costituita da un valore dell'attributo tratto) Azione, Condizione
Azione ActionBuilder Comando, Entità, Espressione Parallelo, Seleziona, Sequenziale
Sequenziale SequentialFlowBuilder Parallelo, Seleziona, Sequenziale
Parallelo ParallelFlowBuilder Azione Sequenziale
Condizione ConditionBuilder Espressione Parallelo, sequenziale
Seleziona AutomationBuilder Condition, Sequential, Starter, ManualStarter Sequenziale e deve essere il primo nodo del flusso

DSL di automazione

Nelle API Home, le automazioni vengono definite utilizzando il linguaggio specifico del dominio (DSL) per le automazioni. L'Automation DSL è implementato come Kotlin DSL (linguaggio specifico per il dominio), utilizzando i costruttori sicuri di tipo Kotlin ed è progettato specificamente per definire i modelli di automazione.

Quando un'automazione viene compilata, i costruttori sicuri di tipo Kotlin generano classi di dati Kotlin che vengono poi serializzate in JSON del buffer di protocollo, che viene utilizzato per effettuare chiamate ai servizi di automazione di Google.

Il DSL di automazione semplifica e snellisce il processo di creazione delle automazioni. Utilizza in modo nativo lo stesso modello di dati dei tratti Matter standard e dei tratti smart home presenti nell'API Device.

L'Automation DSL definisce anche la logica di un'automazione in termini di tipi di dispositivi astratti, anziché di istanze di dispositivi specifici situate nella casa di un utente. Consente allo sviluppatore di fornire parametri di input che possono essere utilizzati in fase di esecuzione per specificare le istanze di dispositivi effettive, nonché altri valori di parametro importanti.

La sintassi del DSL è simile a quella di Kotlin ed è altrettanto sicura in termini di tipi, ma un'automazione scritta in Automation DSL è più semplice e concisa rispetto alla stessa automazione scritta in puro Kotlin.

Esempio

Di seguito è riportato un esempio di automazione che accende un dispositivo, scritta utilizzando il linguaggio DSL di automazione:

val automation = automation {
  name = "MyFirstAutomation"
  description = "If light1 is on, turn on light2."
  isActive = true
  sequential {
    val onOffTrait = starter<_>(device1, OnOffLightDevice, OnOff)
    condition() { expression = onOffTrait.onOff equals true }
    action(device2, OnOffLightDevice) { command(OnOff.on()) }
  }
}

Questa automazione è molto di base: quando device1, una luce, si accende (l'attributo onOff si modifica in true), viene inviato il comando on() per accendere device2.

L'automazione utilizza un nodo sequential, che indica che i relativi nodi verranno eseguiti in ordine sequenziale.

All'interno del nodo sequential sono presenti nodi di comportamento come starter, condition e action. L'output del nodo starter viene assegnato a una variabile per l'utilizzo nel nodo condition.