I Template, parte tre: modelli avanzati con Jinja2 su Home Assistant

Leggi questo articolo grazie alle donazioni di Ivan Vannicelli, Andrea Armellin, Mauro Vanzelli, Matteo Quadri, Ivan Dama, Marco Magherini, Giuseppe Pintore, Enrico Carangi, Franco Magro, Walter Di Matteo, Paolo Cinalli.
♥ Partecipa anche tu alle donazioni: sostieni SaggiaMente, sostieni le tue passioni!

Home Assistant offre la possibilità di creare automazioni in modo piuttosto semplice dato che non richiede di conoscere la programmazione ma di imparare soltanto l'elementare sintassi del formato YAML. Da qualche versione è anche possibile adoperare la GUI per creare automazioni, seppure sia una pratica che sconsiglio a chiunque voglia imparare davvero le basi ed avere un livello di controllo capillare. I file YAML si utilizzano ovunque per la configurazione di Home Assistant ma da sempre

semplicità significa accettare delle limitazioni

Esistono diversi sistemi per superare la schematicità di questo formato ed uno dei più noti è quello di affiancarvi Node-RED. Prima di ricorrere a strumenti di questo calibro è bene sapere che all'interno di Home Assistant e dei suoi file YAML è previsto il supporto ad una versione ridotta di Jinja2. Questo non è un vero e proprio linguaggio di programmazione ma un interprete che ci consente di scrivere dei template in modo ben più strutturato e potente. Io ho iniziato a studiarlo quando ho creato un package per la gestione globale dell'illuminazione, perché mi sono subito reso conto che con YAML sarebbe stato necessario moltissimo codice ripetuto e manuale per eseguire delle operazioni semplici su gruppi di luci eterogenei.

Dove si trova Jinja in Home Assistant?

È molto probabile che abbiate già implementato la sintassi Jinja all'interno del vostro Home Assistant senza saperlo. Le sue istruzioni sono infatti note come "template" o "modelli" e possono essere utilizzate per creare dei service_template o data_template all'interno di automazioni, così come dei value_template per i sensori. A differenza del codice YAML, che risulta statico come una pagina di word, il codice dei template viene interpretato di volta in volta al momento dell'esecuzione, se si tratta di una automazione o script, oppure al cambiamento di stato per i sensori. Questo vuol dire che il valore restituito può cambiare dinamicamente, che è una delle ragioni per cui risulta così utile. Abbiamo già pubblicato due articoli sui template, il primo per descrivere genericamente la sintassi e il secondo per illustrare degli esempi di implementazione nelle automazioni, mentre oggi ci concentreremo su alcuni impieghi un po' più avanzati che sfruttano variabili, cicli, condizioni, liste, filtri ed altro.

Letture necessarie prima di procedere:

Una cosa fondamentale da sottolineare è che con Jinja non è possibile eseguire delle azioni su Home Assistant. L'unica possibilità concreta che ci offre è quella di assegnare ad una proprietà dei file YAML (ad esempio una entity_id, un service o un value) un contenuto dinamico. Capisco che detta così può sembrare una cosa da poco, ma non lo è affatto.

Premessa: l'obiettivo di questo articolo non è quello di fornirvi del codice da copia-incollare ma di farvi comprendere un approccio all'uso più avanzato di template e di Jinja. Per questo motivo consiglio di leggerlo e di fare le varie prove quando indicato per fare un po' di pratica ed arrivare insieme alle conclusioni.

Un primo esperimento

Qualsiasi programmatore sa che la teoria è nulla senza la pratica e fare pratica su Home Assistant può essere molto tedioso, dato che molto spesso significa riavviare il server. Per fortuna esiste un piccolo ambiente di test sempre disponibile all'interno di Strumenti per gli sviluppatori / Modelli, che è una delle pagine che utilizzo con maggiore frequenza anche per il debug o la verifica degli stati dei vari dispositivi. Questa sezione è infatti collegata direttamente all'ambiente di Home Assistant in esecuzione, quindi possiamo utilizzarla per accedere alle informazioni così come si farebbe dentro un template. Ciò che voglio dire è che non serve soltanto a controllare la sintassi ma ci restituisce dei valori di stato aggiornati e che corrispondono esattamente a quelli che avremmo con il medesimo codice all'interno dei file di configurazione.

Ad esempio è possibile trovare tutte le luci accese in questo momento selezionandole in base al loro stato all'interno del dominio light con una semplice riga:

{{ states.light | selectattr('state','equalto','on') | list }}

Vi consiglio vivamente di leggere i due articoli già pubblicati in precedenza sull'argomento e che ho linkato sopra, che vi porteranno a comprendere subito la struttura di questo esempio, ma lo descrivo comunque nel per rinfrescarvelo:

  • le doppie parentesi graffe in apertura {{ e chiusura }} indicano che l'output verrà stampato (in pratica come l'echo o il print su svariati linguaggi)
  • states.light è il dominio in cui sono collezionati su Home Assistant gli stati di tutti gli oggetti light ed allo stesso modo esistono states.sensor, states.binary_sensor, state.automation e così via. Questo restituisce una collezione di elementi con tutti gli attributi che possiedono
  • il carattere "pipe" | indica che l'output del codice a sinistra verrà passato come input su quello a destra: in pratica nel primo caso qui sopra tutti gli oggetti all'interno di states.light vengono dati in pasto al comando a selectattr()
  • selectattr() è un filtro che nell'esempio è stato utilizzato per prendere dalla lista di oggetti ricevuta da states.light solo quelli con proprietà "state" uguale ad "on"
  • list riceve l'output del filtro precedente, ovvero tutte le luci accese, e lo mostra in una lista

La cosa molto importante che voglio ribadire è che la riga di codice qui sopra restituirà un valore diverso per ognuno di voi e soprattutto diverso ogni qual volta ci sarà una luce in più accesa o spenta. Questo ci porta ad un altro vantaggio cruciale di Jinja, che ci consente di scrivere del codice astratto, ovvero che non deve essere modificato o adattato alla propria installazione per funzionare. Tanto per fare un esempio: se condivido con voi una automazione in cui l'action si applica ad alcune entità scritte testualmente in YAML, le dovrete modificare una ad una visto che non saranno sicuramente uguali alle vostre. Se invece ottengo le entità dinamicamente tramite un template Jinja sulla base di una selezione logica (ad esempio se la luce è accesa) allora il risultato sarà generato in base all'installazione di Home Assistant in esecuzione e quindi funzionerà senza modifiche anche da voi.

Troubleshooting elementare

Scrivere in forma astratta nasconde delle insidie dato che bisogna tener conto anche di risultati inattesi dovuti alle diverse configurazioni possibili. Ad esempio il codice qui sopra presenta un potenziale problema dato che nel dominio light si trovano anche dispositivi virtuali come le luci definite con platform: template o platform: group all'interno del file light.yaml (o direttamente nel configuration.yaml se non le avete separate) e queste verranno elencate insieme ai dispositivi reali. Come ho detto non si tratta necessariamente di un problema ma bisogna pensarci fin da subito perché in alcuni casi potrebbe esserlo. Ad esempio supponiamo di voler conoscere gli effetti supportati da ogni luce, prima di tutto dovremmo chiederci: dove si trova questa informazione?

Per fortuna l'editor dei modelli ci offre la possibilità di sondare e trovare da soli quasi tutto, poiché la documentazione di Home Assistant è spesso molto lacunosa nella sua presentazione. Ad esempio possiamo prendere una luce di cui conosciamo l'entity_id e vedere un elenco di tutti i suoi attributi in questo modo:

{{ states.light.plafoniera_studio.attributes }}

Inserite questo codice nel vostro editor di modelli sostituendo a plafoniera_studio l'entity_id di un oggetto light dei vostri. Tra gli attributi elencati noterete anche "effect_list", che contiene proprio quello che stavamo cercando. Per estrarre questa informazione per tutte le luci accese basterà aggiungere un filtro map() che va ad estrapolare il valore di una specifica proprietà, il cui nome va indicato tra apici. Anzi, nel caso specifico ne useremo due dato che effect_list si trova dentro attributes (che si trova a sua volta nell'oggetto light). Ecco dunque come si dovrebbe implementare:

{{ states.light | selectattr('state','equalto','on') | map(attribute='attributes') | map(attribute='effect_list') | list }}

Volendo migliorare il codice si può estrapolare direttamente attributes.effect_list in questo modo:

{{ states.light | selectattr('state','equalto','on') | map(attribute='attributes.effect_list') | list }}

La logica è piuttosto semplice una volta capito il funzionamento del | e conoscendo i filtri utilizzati. Tuttavia le luci virtuali ottenute con template o group non possiedono naturalmente l'attributo effect_list, dunque se ne deve tener conto altrimenti l'array risultante avrà alcuni valori Undefined.

Ci viene in aiuto un altro filtro, ovvero rejectattr(), che definisce in quale condizione il valore risultante verrà scartato nell'output. Nello specifico gli potremmo dire di non accettare lo stato (che corrisponde al valore) della proprietà effect_list se uguale ad Undefined. Metto un invio dopo ogni pipe così da renderlo un po' più chiaro:

{{ 
states.light | 
selectattr('state','equalto','on') | 
rejectattr('attributes.effect_list','equalto',Undefined) | 
map(attribute='attributes.effect_list') | 
list 
}}

Con questa modifica non avremo più il problema di trovarci di fronte ad un valore non definito nella lista, cosa che non generebbe un errore fatale (a differenza degli errori nei file YAML che interrompono l'avvio del sistema) ma potrebbe portare ad un malfunzionamento imprevisto e per questo ancor più difficile da localizzare e risolvere.

Analizziamo i risultati con un ciclo for

Una lista di tanti valori può essere filtrata inline ma anche con un ciclo. Per una maggiore comprensione del codice in questo caso è opportuno prima di tutto memorizzare il soggetto del ciclo in una variabile. Useremo il comando set, che ha una sintassi semplicissima:

{% set luci_accese = states.light | selectattr('state','equalto','on') | list %}

Come potete notare, ho preso il codice del template precedente senza i filtri map() e rejectattr() e l'ho messo dopo set luci_accese = dove 'luci_accese' è un nome arbitrario per la variabile che ho scelto io. Importante: io sconsiglio di mischiare italiano ed inglese lo sto facendo solo in sede di questa guida per evidenziare in modo inequivocabile le istruzioni di sistema (in inglese) dai nomi comuni a discrezione dell'utente (in italiano). Fate anche attenzione al fatto che questo comando non deve stampare nulla a schermo e dunque si trova compreso tra {% e %} invece che tra le doppie parentesi graffe. Inserendo solo questo nell'editor di modelli non si vedrà effettivamente apparire nulla, ma potremo utilizzare la variabile come soggetto per le istruzioni successive.

{% set luci_accese = states.light | selectattr('state','equalto','on') | list %}
{% for luce in luci_accese %}
{{ luce.entity_id }}
{% endfor %}

Il codice qui sopra è una implementazione basilare del ciclo for dove si specifica che per ogni valore in luci_accese (che ricordo essere un array) si genererà una variabile con nome luce a cui corrisponderà una singola esecuzione del blocco di codice che termina con un endfor. All'interno del ciclo possiamo fare quel che vogliamo con quella variabile, nel caso specifico io ho stampato a schermo il valore di entity_id per ogni oggetto luce.

A questo punto abbiamo però perso il filtro che ci consentiva di eliminare gli oggetti light senza effetti, ma possiamo reintrodurlo attraverso un controllo if.

{% set luci_accese = states.light | selectattr('state','equalto','on') | list %}
{% for luce in luci_accese %}
  {% if luce.attributes.effect_list != Undefined %}
    {{ luce.attributes.effect_list }}
  {% endif %}
{% endfor %}

Con questa modifica ritorneremo essenzialmente a quanto ottenuto in partenza con l'uso dei filtri ed è probabilmente il tipo di sintassi che molti utilizzerebbero perché più semplice da comprendere. Inoltre spostando dentro il for la logica di scarto dei valori Undefined per effect_list (poiché l'array luci_accese le contiene tutte) ci ritroviamo con l'oggetto light intero e potremo accedere ad altre sue proprietà. Ad esempio potremmo scrivere una cosa potenzialmente inutile ma sicuramente esplicativa come:

{%- set luci_accese = states.light | selectattr('state','equalto','on') | list -%}
{% for luce in luci_accese -%}
  {% if luce.attributes.effect_list != Undefined -%}
    La luce {{ luce.entity_id}} è accesa e supporta i seguenti effetti: {{ luce.attributes.effect_list | join(', ') }}
  {% endif %}
{% endfor %}

Notate che in due punti ho sostituito %} con -%} perché questo specifica all'interprete di ignorare tutti gli spazi vuoti da quel punto fino al primo testo. Non era affatto obbligatorio ma provate a scrivere questo codice senza e noterete la differenza.

Per rendere più leggibile il risultato ho anche dato in pasto la lista luce.attributes.effect_list al modificatore join() specificando di trasformare l'array in un testo in cui ogni valore è legato al successivo da una virgola ed uno spazio (', '). Vi invito a provare con e senza per capire bene la differenza.

Anche in questo caso, però, io scriverei qualcosa di più ottimizzato spostando la verifica della condizione if direttamente nella prima selezione con l'utilizzo dei filtri:

{%- set luci_accese = states.light | selectattr('state','equalto','on') | rejectattr('attributes.effect_list','equalto',Undefined) | list -%}
{% for luce in luci_accese %} 
La luce {{ luce.entity_id}} è accesa e supporta i seguenti effetti: {{ luce.attributes.effect_list | join(', ') }}
{% endfor %}

E volendo mostrare il friendly_name invece dell'entity_id (che a differenza del primo si trova a livello di oggetto e non nei suoi attributi) si potrebbe migliorare il codice prelevando solo il blocco di attributes con il filtro map():

{%- set luci_accese = states.light | selectattr('state','equalto','on') | rejectattr('attributes.effect_list','equalto',Undefined) | map(attribute='attributes') | list -%}
{% for luce in luci_accese %} 
La luce {{ luce.friendly_name}} è accesa e supporta i seguenti effetti: {{ luce.effect_list | join(', ') }}
{% endfor %}

Utilizziamo Jinja su Lovelace con il markdown

I template possono essere anche mostrati all'interno nella nostra "casa". Si possono usare come valori per le variabili, sensori, ecc... oppure si possono inserire in una card di tipo markdown. Questo ci consente di applicare un minimo di formattazione creando elenchi, inserendo grassetto e così via. Ad esempio potremmo far apparire il template realizzato nell'interfaccia usandolo come contenuto di una card nel file YAML di lovelace in questo modo:

  - type: markdown
    content: >
      {%- set luci_accese = states.light | selectattr('state','equalto','on') | rejectattr('attributes.effect_list','equalto',Undefined) | map(attribute='attributes') | list -%}
      {% for luce in luci_accese -%}
        - La luce **{{ luce.friendly_name}}** è accesa e supporta i seguenti effetti: {{ luce.effect_list | join(', ') }}
      {% endfor %}

Nel codice qui sopra ci sono due cose degne di nota:

  • Dato che con il markdown è molto importante anche la gestione del formato e degli allineamenti, ho inserito alcuni trattini che eseguono il trim di spazi e righe vuote dove necessario. Si può anche utilizzare content: >- che equivale ad applicarli dovunque in un colpo solo, ma poi non si avrebbe il ritorno a capo dove serve
  • Ho inserito due elementi di formattazione markdown, ovvero il trattino prima di ogni riga che la trasforma in un punto elenco e il doppio asterisco prima e dopo il friendly_name che lo farà apparire in grasssetto

Il risultato in questo momento sulla mia installazione è il seguente:

  • La luce Plafoniera Cucina è accesa e supporta i seguenti effetti: Slow Temp, Stop
  • La luce Plafoniera Studio è accesa e supporta i seguenti effetti: Slow Temp, Stop
  • La luce Plafoniera Studio Ambilight è accesa e supporta i seguenti effetti: Strobe color, Police, Christmas, RGB, Random Loop, Fast Random Loop, LSD, Slowdown, Disco, Strobe epilepsy!, Alarm, Police2, WhatsApp, Facebook, Twitter, Slow Temp, Stop

Ora questo esperimento può essere interessante per estrapolare delle informazioni ma non è molto utile da visualizzare tutti i giorni, dunque proviamo ad ottenere qualcosa di più concreto, come l'elenco dei sensori con batteria scarica. Andremo questa volta a pescare nel dominio states globale cercando in ogni dispositivo la presenza dell'attributo battery_level ed il suo eventuale stato. Vi evito la descrizione riga per riga in quanto in questo primo approccio al problema utilizzerò solo concetti già noti. Aggiungo solo un piccolo trick che è la conversione in stringa con | string del parametro battery_level, grazie al quale posso evitare di controllare se il device non ha quella proprietà (ad esempio per quelli senza batteria) dato che in quel caso lo trasformerà in un testo vuoto e non si genererà un errore nella verifica successiva con if.

- type: markdown
  title: Batterie < 50%
  content: >
    {%- for dev in states -%}
      {%- set batt = dev.attributes.battery_level | string -%}
      {%- if batt != '' and (batt|int) <= 50 -%}
        - **{{ dev.attributes.friendly_name }}** {{dev.attributes.battery_level }}%{{'\n'}}
      {%- endif -%}
    {%- endfor -%}

Un metodo più corretto

Sto seguendo diverse tappe non strettamente necessarie ma che ritengo utili per introdurre passo per passo nuovi comandi e migliorare le vostre capacità logiche e la sintassi. Ad esempio invece di eseguire il controllo qui sopra per il valore battery_level solo dopo aver preso tutti i dispositivi in states, si può direttamente selezionare quelli che hanno quel valore già definito utilizzando selectattr(). Ecco come diventerebbe il codice:

{%- set con_batteria = states | selectattr("attributes.battery_level", 'defined') -%}
{%- for dispositivo in con_batteria -%}
  {%- if (dispositivo.attributes.battery_level|int) <= 50 -%} 
    - **{{ dispositivo.attributes.friendly_name }}** {{dispositivo.attributes.battery_level }}%{{'\n'}} 
  {%- endif -%}
{% endfor -%}

Un ulteriore affinamento è quello di spostare il test sul valore minimo di batteria nella selezione iniziale, cosa che ci porta ad avere direttamente una lista dei soli dispositivi che ci interessano eliminando tutto il blocco if. Ecco come:

{% set batteria_scarica = states | selectattr("attributes.battery_level", 'defined') | selectattr("attributes.battery_level", '<', 50) | map(attribute='attributes') %}
{%- for dispositivo in batteria_scarica -%}
  - **{{ dispositivo.friendly_name }}** {{dispositivo.battery_level }}%{{'\n'}}
{%- endfor -%}

Se invece si volesse ottenere esclusivamente la lista degli entity_id dei dispositivi interessati da questa specifica selezione, magari per metterli nel value_template di un sensore, il tutto si potrebbe ridurre ad un solo comando rispetto alle sei righe iniziali.

{{ states | selectattr("attributes.battery_level", 'defined') | selectattr("attributes.battery_level", '<', 50) | map(attribute='entity_id') | join(', ') }}

Il punto è che esistono diversi modi per arrivare al medesimo risultato e più si conoscono i vari strumenti più si riesce ad ottimizzare e compattare il codice per l'uso che se ne intende fare. Ad esempio per l'uso in card con il markdown l'ideale sarebbe trasformare l'output in un array con list e poi verificare il numero di elementi con batteria scarica così da mostrare un testo informativo qualora non ce ne siano. Inoltre nell'uso reale conviene sicuramente spostare il limite di segnalazione sotto al 50%, magari intorno al 30%. Ecco il codice che effettivamente utilizzo io:

- type: markdown
  title: Batterie scariche
  content: >
    {%- set batt = states | selectattr("attributes.battery_level", 'defined') | selectattr("attributes.battery_level", '<', 30) | list -%}
    {%- if batt|length > 0 -%}
      {%- for dispositivo in batt -%}
        - **{{ dispositivo.attributes.friendly_name }}** {{dispositivo.attributes.battery_level }}%{{'\n'}}
      {%- endfor -%}
    {%- else -%}
      Nessun dispositivo ha meno del 30% di carica
    {%- endif -%}

Un comportamento (bug) di cui tener conto

Purtroppo l'implementazione di jinja all'interno di Home Assistant è tutt'altro che completa, difatti se lo studiate per intero noterete che moltissimi metodi e filtri non sono supportati (ad esempio non funzionano union, combine, combinations, subelements. shuffle, match, ecc..). In più c'è un comportamento assurdo proprio nei cicli che non so se sia voluto o meno ma crea molti problemi ed è la limitazione di accesso in scrittura alle variabili esterne al ciclo. Forse deriva da qualche implementazione di sicurezza ma onestamente lo considero un vero e proprio bug. Se aprite l'editor di modelli e incollate il seguente codice capirete subito a cosa mi riferisco:

{%- set lista = range(10) | list -%}
Array {{ lista }}
{%- set contatore = 0 -%}
{%- for elemento in lista %}
  elemento {{elemento}} è in posizione {{ contatore }}
{%- set contatore = contatore + 1 -%}
{%- endfor -%}

Il primo rigo crea un array vuoto con 10 elementi con il comando range(). Poi lo stampa nel secondo rigo e nel terzo definisce una variabile contatore con valore iniziale di 0. A seguire il ciclo for analizza tutti e 10 gli elementi aggiungendo 1 al contatore, ma quando ne stampa il contenuto questo rimane sempre a 0. E il risultato si ottiene anche inizializzando un array prima del ciclo cercando poi di aggiungere dei nuovi elementi al suo interno.

Questo tipo di sintassi viene utilizzata spesso in programmazione ma con l'implementazione Jinja su Home Assistant non va bene. Ecco perché sto facendo il possibile per farvi capire come filtrare le liste alla fonte (cosa che abbiamo fatto per il caso delle batterie scariche).

Scrivere dinamicamente una lista di elementi target con Jinja

Uno dei più interessanti utilizzi avanzati dei template è quello che ci consente di eseguire dei comandi su una serie di entità selezionate in modo dinamico. Ad esempio potremmo decidere di voler modificare l'intensità della luce in un dato momento o ad una specifica ora del giorno, ma agendo soltanto sulle luci già accese. Abbiamo già visto come selezionarle ma adesso vi propongo un metodo diverso per introdurre due nuovi filtri, ovvero dict() e groupby(). Anche questi su Home Assistant sono limitati rispetto alle opzioni complete che esistono sull'interprete Jinja2, ma ci offrono comunque delle possibilità interessanti.

{{ dict(states.light|groupby("state"))["on"] | 
rejectattr('attributes.effect_list','equalto',Undefined) |
map(attribute='entity_id') | 
join(', ') }}

Nel codice qui sopra le novità rilevanti sono tutte nella prima riga dove dict() crea un dizionario con gli elementi states.light organizzati in base al loro stato tramite groupby() selezionando poi solo quelli in cui questo è su 'on'. Successivamente ho scartato gli elementi fittizzi in modo empirico basandomi sul fatto che solo i dispositivi light reali possiedono l'attributo effect_list. Non so se esista qualche eccezione ma finora non mi è mai capitato di incontrarla e, finché non troverò un metodo migliore per differenziarli, continuerò ad usare questo al bisogno.

Nell'ultima riga invece di stampare l'array in lista ho utilizzato ancora join(', ') per ottenere l'elenco degli id interessati in sequenza separati da una virgola e spazio, che è un formato adatto all'utilizzo nella action di una automazione. Se preferite è anche possibile stamparli uno per riga con un trattino all'inizio modificando il join in questo modo:

- {{ dict(states.light|groupby("state"))["on"] | rejectattr('attributes.effect_list','equalto',Undefined) | map(attribute='entity_id') | join('\n- ') }}

Quindi un esempio concreto di utilizzo con una automazione che alle 18:30 riduce al 50% l'intensità delle sole luci già accese potrebbe essere il seguente:

automation:
  - alias: Luce tenue nel pomeriggio
    trigger:
      platform: time
      at: "18:30:00"
    action:
      service: light.turn_on
      data_template:
        entity_id: >
          - {{ dict(states.light|groupby("state"))["on"] | rejectattr('attributes.effect_list','equalto',Undefined) | map(attribute='entity_id') | join('\n- ') }}
        brightness_pct: 50

In effetti non ho testato questa specifica automazione perché io utilizzo un sistema ben più complesso per gestire intensità e temperatura delle luci ma in teoria dovrebbe funzionare!

Conclusioni

Il templating con Jinja2 su Home Assistant ci offre molte possibilità per rendere le automazioni e i sensori più potenti e dinamici. Il dialogo qui esposto con i vari test e stralci di codice vuole essere solo uno stimolo, un modo per introdurre un approccio più avanzato all'uso di queste tecniche all'interno delle proprie configurazioni. Non è una guida completa e neanche si può dire che gli esempi citati abbiano tutti applicazioni concrete, tuttavia spero che la lettura possa fornire una base di partenza per scrutare le potenzialità ed avere un'idea sommaria degli strumenti principali a disposizione, di cosa si può fare e di come procedere anche per tentativi fino ad ottenere il risultato sperato.

Quello che effettivamente manca o è di difficile reperimento è un elenco di tutti i metodi disponibili nell'interprete di Home Assistant con le relative limitazioni. Esiste infatti una documentazione ufficiale sui template Jinja2 ed una specifica di Home Assistant con alcuni esempi, ma non sono effettivamente descritte tutte le possibilità, i metodi, i filtri e soprattutto non tutte le opzioni di questi ultimi. Io molte cose le ho scoperte soltanto provando, un po' ad intuito un po' per pregresse conoscenze di programmazione, per questo ho voluto scrivere questo articolo non come un elenco tabellare ma piuttosto come un dialogo procedurale finalizzato a farvi comprendere un metodo più che un elenco di "come si fa".

Maurizio Natali

Titolare e caporedattore di SaggiaMente, è "in rete" da quando ancora non c'era, con un BBS nell'era dei dinosauri informatici. Nel 2009 ha creato questo sito nel tempo libero, ma ora richiede più tempo di quanto ne abbia da offrire. Profondo sostenitore delle giornate di 36 ore, influencer di sé stesso e guru nella pausa pranzo, da anni si abbronza solo con la luce del monitor. Fotografo e videografo per lavoro e passione, seguimi su Instagram