Concetti DSL di Android

Questa è una panoramica dei concetti fondamentali della DSL di automazione su Android.

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, ad esempio una modifica a 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 essere valutata come true affinché le azioni di un'automazione possano procedere.
  3. Azione : comandi o aggiornamenti di stato eseguiti quando tutte le condizioni sono state soddisfatte.

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

  1. Comando iniziale : la TV è stata accesa, il che rappresenta una modifica dello stato di un tratto della TV.
  2. Condizione : viene valutata 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 nella stanza viene accesa, ma viene eseguita solo se la condizione "l'ora è compresa tra il tramonto e l'alba" è soddisfatta.

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

Nodi

Nelle API Home, la struttura logica di un'automazione è costituita da nodi. I nodi sono unità astratte e 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 Kotlin Descrizione
Comando iniziale Comportamentale StarterNodeDsl Avvia un'automazione quando lo stato di un tratto (qualsiasi attributo) cambia.
StateReader Comportamentale StateReaderNodeDsl Legge un attributo del tratto e ti consente di acquisirne il valore per utilizzarlo nei nodi delle condizioni.
Azione Comportamentale ActionNodeDsl Richiama i comandi del tratto.
Sequenziale Flusso di esecuzione SequentialFlow Esegue i nodi delle azioni nidificate in sequenza. Questo è il comportamento di esecuzione comportamento.
Parallelo Flusso di esecuzione ParallelFlow Esegue i nodi delle azioni nidificate 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 del comando iniziale) o essere globali (si applicano a tutti i comandi iniziali).
Seleziona Flusso di esecuzione SelectFlow Consente a più di un comando iniziale di attivare un'automazione.
Espressione Valore Expression Può essere il valore di un attributo del tratto, una costante o un valore letterale, e deve essere valutato come un elenco, un numero, un valore booleano o una stringa.

Nodi comportamentali

I nodi come i comandi iniziali e le azioni sono nodi comportamentali. I comandi iniziali attivano un'automazione in base alle modifiche degli attributi del dispositivo. Le azioni emettono comandi del dispositivo o aggiornano gli attributi.

I nodi comportamentali sono in genere associati ai tratti del dispositivo e allo stato del tratto di output da utilizzare come input in altri nodi.

Nodi del flusso di esecuzione

Alcuni nodi rappresentano i flussi di esecuzione, come quelli sequenziali e paralleli. Ciascuno di questi nodi contiene i nodi comportamentali 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 sequenziale
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 vengono eseguiti finché non vengono completati tutti i rami del flusso parallelo.

Flussi di esecuzione parallela
Figura 2: flusso di automazione parallelo

Un altro tipo di flusso di esecuzione è un flusso di condizioni, 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 o meno. Un nodo di condizione controlla l'ora del giorno, quindi segue il percorso di esecuzione appropriato in base a questa valutazione.

Flusso di condizioni
Figura 3: flusso di 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 dei comandi iniziali può attivare l'automazione.

Ad esempio, puoi scrivere un'automazione che abbassa le tapparelle 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 sarebbero racchiusi in un flusso select.

Seleziona flusso
Figura 4: flusso di selezione

Flussi nidificati

Nelle automazioni complesse, i nodi del flusso di esecuzione possono anche essere nidificati. Ad esempio, potresti 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 indicati nella tabella seguente. La colonna Builder rimanda alla documentazione del builder Kotlin typesafe, che descrive in dettaglio cosa è consentito utilizzare in ogni tipo di nodo.

Tabella: come combinare i nodi
Nodo Può contenere il seguente tipo di nodo e dati Deve essere all'interno di uno dei seguenti tipi di nodi
Comando iniziale Espressione Seleziona, Sequenziale
ManualStarter Seleziona, Sequenziale
StateReader Espressione (in genere costituita da un valore dell'attributo del tratto) Azione, Condizione
Azione Comando, Entità, Espressione Parallelo, Seleziona, Sequenziale
Sequenziale Parallelo, Seleziona, Sequenziale
Parallelo Azione Sequenziale
Condizione Espressione Parallelo, Sequenziale
Seleziona Condizione, Sequenziale, Comando iniziale, ManualStarter Sequenziale e deve essere il primo nodo del flusso

DSL di automazione

Nelle API Home, le automazioni vengono definite utilizzando la DSL di automazione (Domain-Specific Language). La DSL di automazione viene implementata come DSL Kotlin (Domain-Specific Language), utilizzando i builder Kotlin typesafe ed è progettata specificamente per la definizione dei modelli di automazione.

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

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

La DSL di automazione definisce anche la logica di un'automazione in termini di tipi di dispositivi astratti, anziché di istanze di dispositivi specifici che si trovano nella casa di un utente. Consente allo sviluppatore di fornire parametri di input che possono essere utilizzati in fase di runtime per specificare le istanze di dispositivi effettivi, nonché altri valori di parametri importanti.

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

Esempio

Di seguito è riportato un esempio di automazione che accende un dispositivo, scritto utilizzando la 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 semplice: quando device1, una luce, si accende (l'attributo onOff diventa true), invia il comando on() per accendere device2.

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

All'interno del nodo sequential sono presenti nodi comportamentali come starter, condition e action. L'output del nodo starter viene assegnato a una variabile da utilizzare nel nodo condition.