Uno script, in Home Assistant, è una sequenza riutilizzabile di azioni. La prima cosa che salta all’occhio confrontando i tre editor — e si vede bene mettendoli a confronto — è che uno script non ha trigger né condizioni proprie, solo una sequenza. Le automazioni invece aggiungono trigger e condizioni. Le scene, diversamente da entrambi, sono una fotografia dello stato di device ed entità in un preciso momento, non una sequenza di azioni. Per approfondire la differenza con le scene, la guida alle scene di Home Assistant entra nel dettaglio dell’editor.
Come si crea uno script (UI e YAML)
Per creare uno script, come al solito, si va su Impostazioni → Automazioni e scene. Si sceglie in alto il tab Script e in basso a destra Crea nuovo script.
Come esempio ho creato uno script elementare per l’accensione classica delle luci con transizione di 3 secondi. L’ho chiamato “Soggiorno test script” — puoi inserire qualsiasi nome, a differenza di qualche anno fa. Home Assistant genera automaticamente la formattazione tecnica richiesta dell’object_id (minuscolo, con underscore) e salva il nome che hai scritto come alias.
soggiorno_test_script:
sequence:
- action: light.toggle
metadata: {}
target:
entity_id: light.soggiorno
data:
brightness_pct: 100
transition: 3
alias: Soggiorno test script
description: ''Nota: il vincolo “niente maiuscole né trattini” vale solo se scrivi lo script a mano direttamente in
configuration.yaml, senza passare dall’editor grafico — lì la chiave la scrivi tu e deve rispettare quelle regole. Dalla UI la conversione è automatica e non devi pensarci.
Sintassi di base: sequence e action
Come visto nel paragrafo precedente, la sintassi può essere davvero minima. Per uno script Home Assistant è sufficiente una sola azione perché funzioni perfettamente — niente trigger, niente condition.
Procediamo con un altro esempio, sempre partendo dall’accensione delle luci ma aggiungendo anche una notifica su telefono (ed eventualmente sull’orologio). Uno script semplice e minimal, riutilizzabile in tante automazioni senza dover riscrivere il codice ogni volta — basta richiamarlo.
Una nota importante: rispetto all’esempio precedente ho sostituito light.toggle con light.turn_on — il primo commuta lo stato da on a off e viceversa, il secondo porta sempre e solo lo stato a on.
sequence:
- action: light.turn_on
metadata: {}
target:
entity_id: light.soggiorno
data:
brightness_pct: 100
color_temp_kelvin: 2000
transition: 3
- action: notify.mobile_app_iphone_di_massimo
metadata: {}
data:
message: Luci in soggiorno Accese
title: Notifica da Home Assistant
alias: Soggiorno test script
description: ''La stessa notifica arriva anche sull’Apple Watch, senza configurare nulla di aggiuntivo.
Campi e variabili: rendere lo script riutilizzabile
Negli script si possono usare due proprietà che li rendono più leggibili e riutilizzabili.
Variabili
La prima sono le variabili, come in questo esempio:
sequence:
- variables:
colore_scena: "caldo"
- action: light.turn_on
target:
entity_id: light.soggiorno
data:
color_temp_kelvin: "{{ 2200 if colore_scena == 'caldo' else 4000 }}"Qui, dentro il codice, decidiamo una variabile a cui assegniamo un valore, che poi possiamo riutilizzare più avanti senza doverlo riscrivere.
Un esempio più complesso, con variabile calcolata:
sequence:
- variables:
luminosita_calcolata: >-
{{ 30 if (now().hour >= 21 or now().hour < 6) else (100 if now().hour >= 9 else 60) }}
- action: light.turn_on
metadata: {}
target:
entity_id: light.soggiorno
data:
transition: 3
brightness_pct: "{{ luminosita_calcolata }}"
- action: notify.mobile_app_iphone_di_massimo
metadata: {}
data:
message: Luci in soggiorno Accese
title: Notifica da Home AssistantCome funziona questa espressione:
now().hourrestituisce l’ora corrente come numero intero, da 0 a 23 (le 14:30 diventano 14).- Primo controllo,
now().hour >= 21 or now().hour < 6: vero se sono le 21 o più tardi, oppure le 6 del mattino o prima (fascia notturna: 21, 22, 23, 0, 1, 2, 3, 4, 5). Se vero, il risultato è 30 e la valutazione finisce lì. - Se il primo controllo è falso (siamo tra le 6 e le 8, oppure dalle 9 in poi), si valuta la parte tra parentesi,
100 if now().hour >= 9 else 60: se l’ora è maggiore o uguale a 9 il risultato è 100, altrimenti — fascia 6-8, l’unica rimasta — è 60.
| Fascia oraria | Luminosità applicata | Condizione che scatta |
|---|---|---|
| 21:00 – 23:59 | 30% | prima condizione (>= 21) |
| 00:00 – 05:59 | 30% | prima condizione (< 6) |
| 06:00 – 08:59 | 60% | seconda condizione, ramo else |
| 09:00 – 20:59 | 100% | seconda condizione, ramo if |
21:00 – 23:59
00:00 – 05:59
06:00 – 08:59
09:00 – 20:59
Campi
I fields (campi) sono diversi dalle variabili: si dichiarano a livello dello script, non dentro la sequenza, e servono a intercettare un valore che chi chiama lo script deve o può passare da fuori.
Si creano dal menu a tre puntini in alto a destra dell’editor dello script — lo stesso da cui si accede a “Modifica in YAML”. Il percorso è questo:
- Apri lo script nell’editor.
- Clicca i tre puntini in alto a destra.
- Seleziona “Aggiungi campi”.
- Compare una nuova sezione Campi, tra le info base e la Sequenza.
- Per ogni campo inserisci nome, descrizione, se è obbligatorio, un valore d’esempio e il tipo di selettore (number, text, entity, ecc.).
In questo esempio il campo creato è un number per scegliere la luminosità da passare allo script:
fields:
luminosita:
selector:
number:
min: 1
max: 100
unit_of_measurement: '%'
default: 50
name: luminositaQuando lo script viene richiamato si apre un selettore di luminosità: il valore scelto è quello che passeremo all’azione light.turn_on.
Il codice completo dello script con il campo:
sequence:
- action: light.turn_on
metadata: {}
target:
entity_id: light.soggiorno
data:
transition: 3
brightness_pct: '{{ luminosita | int(100) }}'
- action: notify.mobile_app_iphone_di_massimo
metadata: {}
data:
message: Luci in soggiorno Accese
title: Notifica da Home Assistant
alias: Soggiorno test script
description: ''
fields:
luminosita:
selector:
number:
min: 1
max: 100
unit_of_measurement: '%'
default: 50
name: luminositaLo slider del campo compare al momento della chiamata dello script
Per chiudere il cerchio, ecco come si passa un valore al campo luminosita quando richiami questo script da un’altra automazione o da un altro script:
- action: script.soggiorno_test_script
data:
luminosita: 80Nel prossimo capitolo vediamo nel dettaglio la differenza tra questo tipo di chiamata e l’alternativa con script.turn_on — e perché non è solo una questione di sintassi.
Chiamare uno script: differenza cruciale tra script.NOME e script.turn_on
Per richiamare uno script da un’automazione ci sono fondamentalmente due metodi.
Metodo 1 — Chiamata diretta
- action: script.soggiorno_test_scriptCon questo metodo si avvia lo script e l’automazione aspetta il completamento prima di riprendere la propria sequenza. Se nello script c’è un’attesa di 10 secondi, tutta l’automazione si ferma per 10 secondi prima di proseguire. Utile, ma con un effetto collaterale da conoscere: se lo script fallisce, l’errore si propaga e tutta l’automazione fallisce con esso.
Metodo 2 — script.turn_on
- action: script.turn_on
target:
entity_id: script.soggiorno_test_scriptL’automazione che richiama lo script non aspetta il completamento, prosegue subito. È il cosiddetto “lancia e dimentica” (fire-and-forget). Se lo script fallisce, l’errore non si propaga. Utile quando vuoi avviare uno script senza bloccare il resto dell’automazione — script “in background”.
Come scegliere, in pratica
| Situazione | Metodo consigliato |
|---|---|
| Vuoi che l’automazione aspetti il risultato prima di proseguire | Chiamata diretta |
| Vuoi il form con gli slider nell’editor, senza scrivere YAML a mano | Chiamata diretta |
| Vuoi lanciare qualcosa senza bloccare il resto dell’automazione | script.turn_on |
| Non vuoi che un errore nello script fermi l’automazione chiamante | script.turn_on |
| Devi chiamare più script insieme con una sola azione (entity_id come lista) | script.turn_on |
Chiamata diretta
script.turn_on
Conseguenza pratica di questa differenza: con la chiamata diretta, l’editor delle automazioni legge i fields dello script e mostra il form con gli slider e i selettori pronti da compilare. Con script.turn_on quel form non compare — devi passare i valori a mano dentro data: variables.
Con script.turn_on puoi anche chiamare più script insieme, senza attendere:
- action: script.turn_on
target:
entity_id:
- script.soggiorno_test_script
- script.cucina_scena_mattinaEntrambi gli script partono contemporaneamente, in modo indipendente l’uno dall’altro.
Le 4 modalità di esecuzione
Ad ogni script si può assegnare una modalità di esecuzione (mode): single, restart, queued, parallel.
- single — usala quando vuoi che lo script venga eseguito una sola volta alla volta. Se hai una routine mattutina con una sequenza di passaggi, non ha senso farla ricominciare da capo o interromperla mentre è in corso: le richieste che arrivano mentre è già attiva vengono semplicemente ignorate.
- restart — si usa quando, durante l’esecuzione, vuoi che lo script venga interrotto e riparta da zero con i nuovi dati.
- queued — quando vuoi mettere in sequenza le richieste, senza scartarle: finita un’esecuzione, si passa alla successiva in coda.
- parallel — quando vuoi che più esecuzioni partano in parallelo, senza attendere il completamento delle precedenti.
| Modalità | Cosa succede se richiamato mentre è già in corso | Quando usarla |
|---|---|---|
| single (default) | Il nuovo richiamo viene ignorato, HA registra solo un avviso nei log. L’esecuzione in corso prosegue indisturbata. | Script dove le sovrapposizioni non hanno senso, come una routine “buongiorno” che non va rilanciata se sta già girando |
| restart | L’esecuzione in corso viene fermata immediatamente e riparte da zero con i nuovi dati. | Script che devono sempre riflettere l’input più recente e scartare il precedente |
| queued | Il nuovo richiamo si mette in coda e parte solo quando quello in corso è finito. Ordine garantito. | Quando ogni richiamo deve essere eseguito per intero, senza perderne nessuno |
| parallel | Parte una nuova esecuzione indipendente, in parallelo a quella già in corso. | Quando le esecuzioni non interferiscono tra loro |
single (default)
restart
queued
parallel
Due parametri aggiuntivi si applicano solo a queued e parallel: max (numero massimo di esecuzioni in coda o in parallelo, default 10) e max_exceeded (livello di log quando quel limite viene superato, default warning, può diventare silent).
Nell’automazione del Tuya Smart Knob ZG-101Z/D uso mode: queued con max: 3: quando ruoti la manopola a destra o sinistra, ogni scatto cambia la luminosità della luce collegata, e in questo contesto queued è la scelta giusta perché mette in sequenza gli step della rotazione. Il valore max: 3 evita sequenze infinite se ruoti molto velocemente.
Con single, tutti gli step che arrivano mentre l’automazione è già in corso verrebbero scartati, facendo sembrare l’automazione piena di “buchi”. Con restart, ogni nuovo scatto azzererebbe quello precedente e la transizione di luminosità risulterebbe a scatti invece che fluida.
Logica avanzata dentro uno script
Lo script può assumere anche un alto grado di complessità: è una vera e propria sequenza logica, dove puoi usare tutti i costrutti di controllo del flusso:
condition— ferma la sequenza se falsaif/then/elseechoose— se… allorarepeat— concount,for_each,while,untilwait_templateewait_for_trigger— più la variabilewait.completeddelay— pausaparallel— azioni simultanee, con i caveat ufficiali su ordine ed errori
condition — fermare la sequenza se una condizione è falsa
La prima è condition: ferma uno script se la condizione data non risulta vera. Esempio con il sensore della porta binary_sensor.portone_varco_contact: spegni la luce solo se il portone è chiuso. Se è aperto, lo script si ferma e non fa nient’altro.
- condition: state
entity_id: binary_sensor.portone_varco_contact
state: "off"
- action: light.turn_off
target:
entity_id: light.soggiornoif/then/else e choose — logica condizionale vera e propria
La stessa identica sintassi che vedi qui sotto funziona sia dentro uno script sia dentro un’automazione. I due esempi che seguono sono presi da un’automazione reale, “Accensione luce di cortesia esterna”, proprio perché lì è più immediato vedere trigger e scelta insieme.
L’automazione governa una luce esterna che si accende quando c’è movimento e si spegne quando non ce n’è più: due trigger, on e off. Importante: quando i rami di scelta devono far riferimento ai trigger creati in precedenza, ogni trigger ha bisogno di un ID.
Esempio con choose:
- choose:
- conditions:
- condition: trigger
id:
- 'on'
sequence:
- action: scene.turn_on
metadata: {}
data: {}
target:
entity_id: scene.esterno_luce_di_cortesia
- conditions:
- condition: trigger
id:
- 'off'
sequence:
- delay:
hours: 0
minutes: 0
seconds: 2
milliseconds: 0
- action: light.turn_off
metadata: {}
data: {}
target:
entity_id:
- light.yeelight_color_0x3707763_esternoEsempio con if, doppia condizione che devono essere entrambe vere:
actions:
- if:
- condition: and
conditions:
- condition: occupancy.is_detected
target:
entity_id: binary_sensor.0xa4c138e9e1c7c89d_presence
options:
behavior: any
for: '00:00:00'
- condition: numeric_state
entity_id: sensor.0xa4c138e9e1c7c89d_illuminance
below: 50
then:
- action: light.turn_on
metadata: {}
target:
entity_id: light.yeelight_color_0x3707763_esterno
data:
brightness_pct: 100repeat — ripetere un blocco di azioni
count— ripeti un numero fisso di volte, ad esempio far lampeggiare una luce 3 voltefor_each— itera su una lista, applicando la stessa azione a più entitàwhile— ripeti finché una condizione resta vera, valutata prima di ogni ciclountil— ripeti finché una condizione diventa vera, valutata dopo il ciclo, quindi gira almeno una volta
Esempio con i miei termostati: imposto accensione e temperatura su entrambe le valvole Sonoff TRVZB in un colpo solo con repeat: for_each.
sequence:
- repeat:
for_each:
- climate.aqara_termostato
- climate.0x3c2ef5fffebad64b
sequence:
- action: climate.set_temperature
metadata: {}
target:
entity_id: '{{ repeat.item }}'
data:
temperature: 24
hvac_mode: heat
alias: New script
description: ''In verità lo stesso risultato si sarebbe potuto ottenere anche senza scomodare for_each — bastava passare una lista di entity_id a climate.set_temperature — ma lo proponiamo come esempio didattico del ciclo.
wait_template e wait_for_trigger — mettere in pausa lo script fino a una condizione
Diverso da delay, che aspetta un tempo fisso: qui lo script aspetta finché qualcosa diventa vero.
- wait_template: "{{ is_state('binary_sensor.portone_varco_contact', 'on') }}"
timeout: "00:05:00"
continue_on_timeout: true
- if:
- "{{ not wait.completed }}"
then:
- action: notify.mobile_app_iphone_di_massimo
data:
message: "Portone rimasto aperto oltre 5 minuti"Esempio reale con il Portone Varco: aspetta che si apra per 5 minuti; se scade il timeout senza che sia successo (wait.completed è false), manda una notifica.
delay — pausa a tempo fisso
Serve a mettere in pausa lo script per un numero determinato di secondi, minuti o ore.
parallel — eseguire più azioni contemporaneamente
parallel esegue le azioni in parallelo, senza aspettare il completamento dell’una o dell’altra. Se una fallisce, l’altra prosegue comunque. Attenzione se le azioni parallele scrivono le stesse variabili: possono entrare in conflitto.
Gestione errori: continue_on_error e stop
Nel comportamento di default, se c’è un errore lo script si ferma — ma è possibile aggirarlo con continue_on_error e stop.
sequence:
- repeat:
for_each:
- climate.aqara_termostato
- climate.0x3c2ef5fffebad64b
sequence:
- action: climate.set_temperature
metadata: {}
continue_on_error: true
target:
entity_id: '{{ repeat.item }}'
data:
temperature: 24
hvac_mode: heat
alias: New script
description: ''continue_on_error va posizionato su ogni singola azione, non su tutto lo script: ha effetto solo per quella specifica azione.
Fermare volontariamente la sequenza: stop
Diverso da un errore: qui sei tu a decidere di interrompere lo script a un certo punto, ad esempio dentro un if quando una condizione non è soddisfatta.
- if:
- condition: state
entity_id: binary_sensor.portone_varco_contact
state: "on"
then:
- stop: "Portone aperto, non ha senso continuare"Esempi pratici: script reali dalla mia installazione
Ti fornisco alcuni esempi di codice ricavati direttamente dai miei script — solo per darti un’idea, adattali pure alle tue esigenze.
Script di analisi meteo con AI
Uso AI Task per le previsioni meteo per trasformare i dati dei sensori barometrici in una previsione leggibile:
sequence:
- action: input_text.set_value
metadata: {}
data:
value: Valutazione Meteo in Corso dai Sensori Barometrici.....
target:
entity_id: input_text.ai_meto
- action: ai_task.generate_data
metadata: {}
data:
task_name: analisi_barometrica
instructions: >
Analizza i seguenti dati barometrici:
- Pressione attuale: {{ states('sensor.temperatura_n_2_aqara_pressure') }} hPa
- Pressione 1h fa: {{ states('sensor.pressione_1h_fa') }} hPa
- Pressione 3h fa: {{ states('sensor.pressione_3_ore_fa') }} hPa
- Trend 1h: {{ states('sensor.tendenza_barometrica_1h') }} hPa/h
- Trend 3h: {{ states('sensor.tendenza_barometrica_3h') }} hPa/3h
- Temperatura: {{ states('sensor.temperatura_n_2_aqara_temperature') }}°C
- Umidità: {{ states('sensor.temperatura_n_2_aqara_humidity') }}%
- Importante: luogo di misurazione pressione barometrica a 300 metri sul
livello del mare, tienine conto.
Genera una previsione meteo dettagliata basata sui trend barometrici.
Usa le regole WMO per interpretare le variazioni di pressione.
Per nessun motivo superare i 250 caratteri, spazi compresi.
structure:
previsione_breve:
description: Previsione per le prossime 6 ore, max 255 caratteri
required: true
selector:
text: null
tendenza_meteo:
description: Tendenza generale (miglioramento/peggioramento/stabile)
required: true
selector:
select:
options:
- miglioramento
- peggioramento
- stabile
- variabile
probabilita_pioggia:
description: Probabilità precipitazioni in percentuale
required: true
selector:
number:
min: 0
max: 100
consigli_pratici:
description: Suggerimenti per attività all'aperto
required: true
selector:
text: null
affidabilita:
description: Livello di affidabilità della previsione (1-5)
required: true
selector:
number:
min: 1
max: 5
entity_id: ai_task.google_ai_task
response_variable: previsione_ai
- action: input_text.set_value
metadata: {}
data:
value: '{{ previsione_ai.data.previsione_breve }}'
target:
entity_id: input_text.ai_meto
- action: input_number.set_value
metadata: {}
data:
value: '{{ previsione_ai.data.probabilita_pioggia }}'
target:
entity_id: input_number.pioggia
alias: Analisi meteo (gemini ai)
description: ''Cambio canzone
sequence:
- action: input_select.select_next
metadata: {}
data:
cycle: true
target:
entity_id: input_select.music_list
alias: Cambia Canzone nella Lista della Musica
description: ''
icon: mdi:book-musicMusica jazz in cucina
Musica jazz in cucina, avviata su Echo tramite Music Assistant:
alias: Musica Cucina Jazz Radio
description: Avvia musica echo amazon
mode: restart
icon: mdi:music-clef-treble
sequence:
- alias: 'Calibrazione Volume a 20'
if:
- condition: numeric_state
entity_id: media_player.echo_plus_di_massimo
above: 0.21
then:
- action: media_player.volume_set
metadata: {}
data:
volume_level: 0.2
target:
entity_id: media_player.echo_plus_di_massimo
- if:
- condition: state
entity_id: media_player.echo_plus_di_massimo
state:
- playing
then: []
else:
- action: media_player.play_media
metadata: {}
data:
media_content_id: Jazz chill radio
media_content_type: APPLE_MUSIC
target:
entity_id:
- media_player.echo_plus_di_massimoQuando usare uno script invece di metterlo dentro l’automazione
L’uso dello script è facoltativo e soggettivo: l’intera sequenza si può scrivere tranquillamente dentro l’automazione stessa. Ma ci sono buoni motivi per separarla.
- Codice più pulito. Se vuoi cambiare qualcosa, vai nello script relativo senza dover mettere mano a tutta l’automazione.
- Riuso. Se più automazioni Home Assistant senza codice devono fare la stessa cosa a un certo punto, con uno script scrivi il codice una volta sola e lo richiami, invece di riscriverlo ogni volta.
- Separare la logica, ad esempio per farti restituire un valore con
response_variable, con o senza attesa. - Suddividere un’automazione complessa e difficile da leggere in step più piccoli.
- Usarlo fuori da un’automazione, ad esempio come pulsante su una dashboard Bubble Card.
| Criterio | Script separato | Dentro l’automazione |
|---|---|---|
| Stessa sequenza usata da più trigger | Consigliato | Porta a duplicazione |
| Logica complessa (choose, repeat, wait) | Più leggibile | Diventa illeggibile |
| Serve testarla isolata dal trigger | Consigliato | Devi riprodurre il trigger |
| Serve un input variabile (fields) | Consigliato | Non esiste equivalente |
| Deve essere richiamabile da dashboard o voce | Consigliato | Non possibile |
| Sequenza breve, un solo trigger, nessuna logica | Overhead inutile | Più diretto |
Stessa sequenza usata da più trigger
Logica complessa (choose, repeat, wait)
Serve testarla isolata dal trigger
Serve un input variabile (fields)
Deve essere richiamabile da dashboard o voce
Sequenza breve, un solo trigger, nessuna logica
Domande frequenti
Uno script può chiamare un altro script? Sì, esattamente come farebbe un’automazione, con chiamata diretta o script.turn_on, incluso il nesting di mode e max sullo script chiamato.
Gli script consumano risorse se non sono attivi? No. Uno script fermo esiste come entità ma non fa nulla finché non viene richiamato.
Posso avere script senza limite? Non c’è un limite software al numero di script che puoi creare: il limite pratico è quanto resta leggibile e manutenibile il tuo setup.
Conclusione
Gli script sono uno strumento potente, quasi di programmazione. Impararli a usare è condizione necessaria per fare di più e meglio con la propria istanza di Home Assistant. Ho cercato di coprire il più possibile gli argomenti, ma la materia è ampia: ti consiglio di dare un’occhiata anche alla documentazione ufficiale sugli script e alla pagina dell’integrazione script, dove trovi il riferimento completo su fields e modalità.
Per il quadro generale su cos’è e come funziona Home Assistant, parti dalla guida introduttiva al software.

