You are currently viewing Multinotify: il package per le notifiche su Alexa e App

Multinotify: il package per le notifiche su Alexa e App

  • Autore dell'articolo:
  • Categoria dell'articolo:Home Assistant
  • Commenti dell'articolo:9 commenti
  • Ultima modifica dell'articolo:21 Gennaio 2022

Richiami spesso dalle tue automazioni notifiche su Alexa e su App? Ti piacerebbe poter scrivere una volta sola il testo della notifica, che la maggior parte delle volte è identica tra app ed Alexa? Ti piacerebbe che Alexa avesse un volume per le notifiche separato da quello impostato sui device?
Questo e molto altro con il package multinotify!

Lo script è nato originalmente perchè nelle mie automazioni richiamavo molto frequentemente sia notifiche sui dispositivi Alexa che su una o più app companion e trovavo scomodo dover richiamare sempre i servizi con le loro sintassi e duplicare il testo della notifica ogni volta.

Poi l’idea di simulare un volume specifico per le notifiche, che Alexa non ha. Per esempio se stai ascoltando la musica di sottofondo con volume 3 ed arriva una notifica farai molta fatica a capirne il contenuto. Da qui l’idea di alzare il volume prima della riproduzione della notifica e ripristinarlo al termine della stessa.

E’ stata anche una buona palestra per imparare diversi concetti avanzati, iniziando dall’articolo che ne è nato, ancora attuale, che spiegava come creare script con parametri opzionali.

In questo articolo, oltre a mettere a disposizione l’ultima versione del package multinotify, ne spiegherò anche in dettaglio come utilizzarlo ed i principi di funzionamento interni, per chi fosse interessato ad approfondire i concetti più avanzati.

Se l’argomento “notifiche” in Home Assistant ti interessa non aspettare oltre e prosegui la lettura!

Caratteristiche del package multinotify

Di seguito quel che lo script Multinotify può fare per te:

  • Notifica ad Alexa e/o app companion potendo specificare una sola volta il testo della notifica
  • Volume della notifica su Alexa specificabile e separato dal volume corrente, che verrà ripristinato al termine della riproduzione
  • Attesa che Alexa termini la pronuncia della notifica. In questo modo le azioni seguenti saranno eseguite a notifica completata. Ad esempio puoi pronunciare “…ora ti alzo le tapparelle” e al termine viene eseguita l’azione.
  • Per Alexa potrai specificare a scelta: una singola entità, un gruppo di entità, un gruppo multistanza interno di Alexa, un elenco di entità o l’entità speciale ovunque.
  • Se stavi ascoltando la musica multinotify la metterà in pausa per te prima della notifica e ripresa al suo termine (idea di Massimiliano Albani: grazie!)
  • Definizione orario in cui Alexa pronuncia le notifiche. Al di fuori dell’orario Alexa non parlerà (a meno che non si forzi la pronuncia con il relativo parametro)
  • Mitigati alcuni problemi di Alexa Media Player impostando un volume all’avvio e aggiornando automaticamente il volume impostato di volta in volta, utile per quando il websocket si disconnette.
  • Al momento le notifiche app non sono compatibili con iPhone.

Installazione e configurazione di multinotify

Installare il package è semplicissimo. Ma prima di tutto verifichiamo che i requisiti siano soddisfatti.

Requisiti

  • Se intendi utilizzare Alexa devi aver installato e configurato Alexa Media Player,
  • Se intendi utilizzare le notifiche dell’app companion devi averne installata e collegata almeno una,
  • Devi aver installato lo script python set_state.
    ATTENZIONE: di script con questo nome ne esistono molte varianti. E’ fondamentale che tu abbia lo script riportato nel mio articolo aggiornato per l’occasione che permette la creazione di entità.

Installazione del package multinotify

Premendo il pulsante sopra scaricherai un file zip contenente il package all’ultima versione.

In tale file zip estrai il file multinotify.yaml ed incollalo nella tua cartella dei packages di Home Assistant.

Se non sai cosa sono i packages fai riferimento a questa sezione della documentazione che spiega come utilizzarli oppure al capitolo “I packages” dell’articolo di Vito Antonucci
Il package è presente nel mio repository GitHub. Se vuoi correggere, implementare nuove feature o segnalare un problema utilizza GitHub 🙂

Configurazione del package multinotify

Ora non resta che configurarlo aprendolo con il tuo editor di testo preferito. La sezione da modificare si trova all’inizio del file, dopo le descrizioni iniziali. Ci sono commenti auto esplicativi ma vediamo di seguito cosa modificare:

group:
  # Gruppo contenente tutti i device Echo presenti (necessario per utilizzare media_player.ovunque come target di multinotify)
  all_alexa:
    name: Tutti i device Echo
    entities:
      # TODO: modificare l'elenco delle entità del gruppo con i propri dispositivi Alexa
      - media_player.pian_terreno
      - media_player.primo_piano
      - media_player.mansarda

Questo è un gruppo fondamentale se si desidera utilizzare l’entità speciale media_player.ovunque (ma non solo!) in quanto lo script utilizza il gruppo per sapere quali dispositivi Alexa hai.
Inserisci quindi tutti i media_player di Alexa Media Player al posto dei tre indicati come esempio.

  # TODO: Definire un gruppo per ogni gruppo interno di Alexa. Per esempio al gruppo media_player.miogruppo dovrà corrispondere
  #       il gruppo group.miogruppo contenente gli stessi dispositivi. Rimuovere l'esempio piani_superiori.
  piani_superiori:
    name: Device Echo dei piani superiori
    entities:
      - media_player.primo_piano
      - media_player.mansarda

Questo è un gruppo esemplificativo di un gruppo interno ad Alexa. Per ogni gruppo altoparlanti – musica multistanza creato dall’app Alexa, infatti, sarà necessario creare un gruppo con stesso nome contenente i dispositivi Alexa di cui è composto, visto che l’integrazione Alexa Media Player non dà modo di accedere a questa informazione.
Se hai più gruppi altoparlanti – musica multistanza configurati in Alexa crea un gruppo per ognuno di essi in questa sezione.

notify:
  # Gruppo notify.all_devices da utilizzare in multinotify per inviare una notifica a tutte le app
  - name: all_devices
    platform: group
    services:
      # TODO: modificare l'elenco dei servizi con i propri servizi di invio notifica alle app Home Assistant Companion collegate
      - service: mobile_app_tel_henrik
      - service: mobile_app_smartphone_antonella

In questa sezione creiamo un servizio di notifica di gruppo che permetterà di specificare l’invio di una notifica a tutte le app senza necessità di elencarle ogni volta che si richiama multinotify. Basterà invece utilizzare notify.all_devices per inviare la notifica a tutte le app.
Se lo desideri è possibile creare altri gruppi (ripeti il blocco a partire da “-name: all_devices” cambiandone nome e servizi contenuti) se hai tante app e desideri creare dei gruppi nominati.

Finito! Sei ora pronto per utilizzare multinotify per il tuo sistema!
Riavvia Home Assistant e prosegui la lettura.

Sintassi dello script multinotify

Come tutti gli script per richiamare multinotify si possono usare due sintassi: service: script.multinotify oppure script.turn_on. La differenza tra i due è che con la prima Home Assistant attende il termine dell’esecuzione dello script, prima di continuare l’esecuzione con l’istruzione successiva. Essendo che una peculiarità di multinotify è che attende il termine della pronuncia da parte di Alexa è consigliato richiamarlo tramite script.multinotify.

Con la seconda sintassi, invece, Home Assistant avvia lo script e prosegue immediatamente con l’istruzione successiva del chiamante.

I parametri vengono dichiarati tutti all’interno di data: e sono i seguenti (separati per categoria):

ComuniAlexaApp companion
ParametroDescrizioneObbligatorioDefault
messageTesto da notificare alle app e pronunciare da Alexa (se non specificato diversamente)SI
titleTitolo della notifica inviata alle appNONotifica da casa
ParametroDescrizioneObbligatorioDefault
alexa_messageTesto che Alexa dovrà pronunciare, se differente da message. Utile se si desidera utilizzare le interiezioni così da limitarle solo alla pronuncia su Alexa e non mostrarle nelle notifiche alle app.NO=message
alexa_targetDispositivi Alexa che dovranno pronunciare il messaggio. E’ possibile specificare una singola entità media_player, un elenco di entità media_player, un gruppo di Home Assistant, un gruppo altoparlanti – musica multistanza di Alexa o entità speciale media_player.ovunqueNOnessuna
alexa_volumeVolume con cui i dispositivi Alexa coinvolti pronunceranno la notifica. Al termine il volume sarà comunque ripristinato a quello precedente di ogni dispositivo Alexa. Valori da 0.1 a 1.0NO0.6
alexa_forceSe non specificato o specificato il valore false il i dispositivi Alexa specificati riprodurranno l’annuncio solo se l’orario corrente è tra le 9:00 e le 23:00. Per pronunciare comunque il messaggio al di fuori di tali orari specificare true.NOfalse
ParametroDescrizioneObbligatorioDefault
notify_appNome del servizio da utilizzare per inviare la notifica alle app companion.NOnessuno
groupStringa identificativa del gruppo di notifiche dell’app. Le notifiche sul telefono verranno raggruppate in base a questo valore.NO=channel o “info” (se channel non è specificato)
channelStringa identificativa del canale per le notifiche all’app. Ogni canale, su Android, può avere impostazioni di notifica (compresa la suoneria) personalizzabili.NO=group o “info” (se group non è specificato)
iconNome del file (senza percorso, senza il prefisso notify_ e senza estensione) da visualizzare come icona della notifica in appNO=channel, =group o “info” se nessuno dei precedenti è definito
tagCampo tag della notifica all’app. Se impostata ogni successiva notifica con pari tag sostituirà la notifica precedente. Utile per notifiche il cui stato cambia e non ha senso mantenere i cambi stato precedenti.NOnessuno
criticalSpecifica lo stato di notifica critica per l’app. Se true l’app mostrerà la notifica appena possibile. Se false l’app mostrerà la notifica alla prima volta in cui avrà modo, ottimizzando il consumo di batteria (possono passare diversi minuti)NOtrue

Preparare le icone da utilizzare per le notifiche di multinotify

Nelle notifiche delle app è utile inserire un’icona per diversificare i diversi tipi di notifica. Multinotify, infatti, supporta l’inserimento di un’immagine nella notifica ma per farlo deve essere un file raggiungibile da internet (quindi la tua istanza di Home Assistant deve essere raggiungibile da internet, la soluzione più semplice è usando Nabu Casa).

Nella cartella config crea, se non già presente, una cartella www. All’interno di tale cartella copia i file png che devono chiamarsi notify_xxxx.png.

Ad esempio per utilizzare l’icona warning è necessario che sia presente il file config/www/notify_warning.png.

Consiglio di utilizzare icone png con trasparenza (png a 32 bit) della dimensione di 48×48 pixel.

Di seguito qualche suggerimento…

Se non sai come accedere alla casella config per inserire le immagini ti consiglio di installare l’addon Samba che trovi in Configurazioni -> Componenti aggiungitivi, backup e supervisore -> RACCOLTA DI COMPONENTI AGGIUNTIVI

Come sperimentare l’utilizzo dei vari parametri…

Tra poco vedremo vari esempi di utilizzo. Per imparare ad usare lo script multinotify per fare esperimenti in modo semplice ti consiglio, una volta installato e configurato, di provare a richiamarlo nelle sue varie modalità tramite Strumenti per sviluppatori -> Servizi, scegliere il servizio “Script: Notifica intelligente” (accessibile anche scrivendo script.multinotify) e utilizzare la modalità interfaccia utente cliccando sul seguente link:

Vai in modalità interfaccia utente

I campi dello script saranno visibili e impostabili semplicemente tramite l’interfaccia utente:

Interfaccia utente dello script multinotify
Nota bene: nella casella Alexa è possibile selezionare soltanto tra i dispositivi Alexa in quanto i selettori di Home Assistant non permettono la versatilità che ho voluto implementare in questo parametro. Se vuoi specificare un elenco o un gruppo dovrai scriverlo a mano in modalità YAML.

Potete vedere il codice YAML corrispondente semplicemente premendo il link in basso

Vai in modalità YAML

In tal modo potrai vedere il codice YAML da richiamare dalle tue automazioni / script con i parametri scelti da interfaccia utente:

Codice YAML generato da Strumenti per sviluppatori dopo aver impostato l'UI dello script multinotify

Ma bando alle ciance, vediamo qualche esempio concreto approfittando dell’occasione per mostrare diverse peculiarità dello script.

Esempi delle varie modalità di utilizzo di multinotify

Utilizzare lo script multinotify è molto semplice. Non lasciarti spaventare dai tanti parametri, quasi tutti sono opzionali!

Vediamo con una serie di esempi vari modi di utilizzo dello script.

La modalità base

Vediamo come utilizzare in modo basilare lo script con alcuni esempi:

service: script.multinotify
data:
  message: Questa è una notifica di prova
  alexa_target: media_player.ovunque
  notify_app: notify.all_devices

Con questo semplice comando verrà inviato un messaggio da ogni dispositivo Alexa della casa e ad ogni app Companion collegata contenente il messaggio specificato.
Per ogni dispositivo Alexa, solo se l’orario è compreso tra le 9:00 e le 23:00, lo script interromperà la riproduzione della musica, se il caso, imposterà il volume al 60%, riprodurrà il messaggio, attenderà il termine della pronuncia ed in seguito ripristinerà i volumi specifici di ogni dispositivo Alexa ed infine riprenderà la riproduzione della musica, se la si stava ascoltando.

Non male no? 🙂

Essendo message l’unico parametro obbligatorio possiamo usare lo script per pronunciare un messaggio solo ad Alexa o inviare una notifica solo alle app.

Se avessimo voluto, ad esempio, far riprodurre il messaggio precedente solo sui dispositivi Alexa sarebbe stato sufficiente richiamarlo così:

service: script.multinotify
data:
  message: Questa è una notifica di prova, inviata solo ad Alexa.
  alexa_target: media_player.ovunque

Varie modalità per specificare i dispositivi Alexa

Lo script è stato sviluppato per permettere la massima flessibilità nel modo in cui specificare su quali dispositivi Alexa riprodurre il messaggio. Di seguito un esempio per ogni possibilità:

Singola entità Alexa

service: script.multinotify
data:
  message: Notifica riprodotta da un solo dispositivo Alexa
  alexa_target: media_player.alexa1

Specificando l’id dell’entità di un singolo dispositivo Alexa, quest’ultimo riprodurrà il messaggio.

Elenco di entità Alexa

service: script.multinotify
data:
  message: "Notifica riprodotta dall'elenco specificato di dispositivi Alexa"
  alexa_target:
    - media_player.alexa1
    - media_player.alexa2

Specificando un elenco di id entità tutti i dispositivi specificati riprodurranno il messaggio specificato.

Gruppo Alexa di Home Assistant

service: script.multinotify
data:
  message: Notifica riprodotta da un gruppo di Alexa definito in Home Assistant.
  alexa_target: group.all_alexa

Specificando un gruppo nel parametro alexa_target, tutti i dispositivi Alexa contenuti in tale gruppo riprodurranno il messaggio.
Questo non è limitato al gruppo all_alexa ma si può usare qualunque gruppo tu voglia definire contenente dispositivi Alexa.

Gruppo altoparlanti – musica multistanza di Alexa

service: script.multinotify
data:
  message: "Notifica riprodotta da un gruppo di altoparlanti definito nell'app Alexa"
  alexa_target: media_player.gruppo_alexa1

Specificando il media_player associato ad un gruppo altoparlanti – musica multistanza di Alexa, tutti i dispositivi che ne fanno parte riprodurranno il messaggio.

Nota bene: è necessario aver impostato un gruppo con pari nome per ciascun gruppo altoparlanti – musica multistanza contenente l’elenco dei dispositivi contenuti. Vedi capitolo configurazione iniziale.

Entità speciale ovunque

service: script.multinotify
data:
  message: Notifica riprodotta da un solo dispositivo Alexa
  alexa_target: media_player.ovunque

L’entità speciale media_player.ovunque altro non è che un gruppo altoparlanti – musica multistanza definito nell’app Alexa per default e contiene tutti i dispositivi Alexa.

Nota bene: è necessario aver impostato il gruppo con nome all_alexa contenente l’elenco di tutti i dispositivi Alexa. Vedi capitolo configurazione iniziale.

Usando tutti i parametri

Vediamo di seguito l’esempio più complesso per richiamare lo script multinotify, utilizzando tutti i parametri presenti:

service: script.multinotify
data:
  title: "Notifica dalla cucina"
  message: "E' pronto da mangiare!"
  alexa_message: '<say-as interpret-as="interjection">hey</say-as>, è pronto da mangiare!'
  alexa_target:
    - media_player.mansarda
    - media_player.primo_piano
  alexa_volume: 0.8
  alexa_force: true
  notify_app: notify.all_devices
  group: meal-cucina
  channel: meal
  icon: meal
  tag: pronto_da_mangiare
  critical: true

Con questi parametri stiamo specificando che:

  • title: “Notifica dalla cucina”
    il titolo della notifica (visibile solo nell’app) sarà “Notifica dalla cucina”
  • message: “E’ pronto da mangiare!”
    il messaggio è “E’ pronto da mangiare!”
  • alexa_message: ‘<say-as interpret-as=”interjection”>hey</say-as>, è pronto da mangiare!’
    per Alexa il messaggio da pronunciare è diverso, così da inserire una interjection che nell’app non sarebbe leggibile
  • alexa_target:
    – media_player.mansarda
    – media_player.primo_piano

    facciamo pronunciare il messagio ai soli due dispositivi Alexa specificati
  • alexa_volume: 0.8
    l’annuncio di Alexa sarà riprodotto con volume 80% e poi sarà ripristinato il singolo volume che ogni dispositivo coinvolto aveva
  • alexa_force: true
    forziamo la riproduzione dell’annuncio Alexa anche se l’orario è al di fuori della fascia 9:00 23:00
  • notify_app: notify.all_devices
    il servizio di notifica da usare. In questo caso notify.all_devices definito ad inizio package, per inviare la notifica a tutte le app
  • group: meal-cucina
    questa notifica farà gruppo tra le notifiche di Android con tutte le notifiche aventi pari valore di questo parametro
  • channel: meal
    Il canale della notifica di Android sarà meal. Tramite la configurazione delle notifiche dell’app sarà possibile impostare una suoneria specifica per questo canale, oltre ad impostazioni come il tipo di vibrazione, il colore del led di notifica ed altro ancora.
  • icon: meal
    Visualizza nella notifica l’icona posta in config/www/notify_meal.png
  • tag: pronto_da_mangiare
    Ogni notifica successiva con questo valore di tag sostituisce la precedente eventualmente ancora presente anzi che aggiungersi come altra notifica. Questo fa sì che richiamando più volte di seguito questo script la notifica presente sarà solo una: la più recente.
  • critical: true
    Questo è già il valore di default per cui è omissibile. true significa che l’app mostrerà la notifica immediatamente.
    Le notifiche aventi invece critical: false possono arrivare dopo diversi minuti, quando l’app risveglia il telefono dallo stato di standby alla prima occasione per verificare se ci sono notifiche.

Azioni sequenziali

Le notifiche di Alexa Media Player non attendono che i dispositivi Alexa pronuncino il testo specificato per proseguire l’esecuzione delle azioni nell’automazione o script. Multinotify inserisce invece un tempo di attesa calcolato in funzione di quanto pronunciato che effettua l’attesa del tempo più simile possible al tempo impiegato da Alexa per la pronuncia. Questo rende possibile sequenze come la seguente:

- service: script.multinotify
  data:
    message: 'Buongiorno! Ora ti alzo le tapparelle!'
    alexa_target: media_player.pian_terreno
    alexa_volume: 0.5
    alexa_force: true
- service: script.tapparelle_apri

Tramite la sequenza sopra il dispositivo Alexa pian_terreno pronuncerà “Buongiorno! Ora ti alzo le tapparelle!” e solo al termine della frase verrà avviato lo script tapparelle_apri. L’effetto è ottimo 🙂

Come funziona lo script multinotify

Finora ho spiegato come installare, configurare ed utilizzare lo script. Spero però davvero che tu colga l’occasione per studiare com’è fatto il package per imparare diverse tecniche che possono diventare molto utili in diversi altri contesti.

Per questo motivo nei capitoli che seguono cercherò di descrivere le parti salienti degli script coinvolti seguendo il flusso di esecuzione, così da capire come funzionano.

Se non l’hai già letto ti consiglio la lettura del precedente articolo sulla prima versione di multinotify, che affronta il tema dei parametri degli script e dei valori di default da assegnare qualora non vengano specificati. Sebbene tale articolo faccia riferimento ad una versione di multinotify superata tutte le nozioni sono utilissime e correntemente utilizzate anche dalle versioni aggiornate: Script con parametri opzionali in Home Assistant

Flusso di funzionamento principale

L’esecuzione dello script multinotify si compone di due parti essenziali: l’invio della notifica alle app e la pronuncia dai dispositivi Alexa.

Notifica alle app

Di seguito il codice che si occupa della notifica alle app:

- alias: "Notifica App"
  choose:
  - alias: "notify_app è stato passato dal chiamante?"
    conditions: "{{ notify_app is defined }}"
    sequence:
      - service: '{{notify_app}}'
        data:
          title: "{{title | default('Notifica da casa')}}"
          message: "{{message}}"
          data:
            group: "{{group | default(channel) | default('info')}}"
            channel: "{{channel | default(group) | default('info')}}"
            tag: "{{tag | default('')}}"
            icon_url: "/local/notify_{{icon | default(channel) | default(group) | default('info')}}.png"
            ttl: "{{0 if (critical | default(true)) else 86400}}"
            priority: "{{'high' if (critical | default(true)) else 'normal'}}"

Il choose determina, tramite la condizione "{{ notify_app is defined }}", che sia stato specificato il parametro notify_app per procedere alla notifica.
In tal caso tramite service template specifichiamo quale servizio notify utilizzare. Il resto è solo una compilazione dei campi necessari, gestendone i valori di default se non specificati dal chiamante.

Notifiche ad Alexa

Il vero cuore, però, di multinotify è nella pronuncia tramite Alexa, che avviene nel blocco seguente che inizia con alias: "Notifica Alexa".

Passiamo in rassegna le varie istruzioni che seguono:

- alias: "Notifica Alexa"
  choose:
    - conditions:
        - alias: "alexa_target è stato passato dal chiamante?"
          condition: template
          value_template: "{{ alexa_target is defined }}"
        - alias: "Orario diurno oppure alexa_force = true"
          condition: or
          conditions:
            - condition: template
              value_template: "{{ alexa_force | default(false)}}"
            - condition: time
              after: '09:00:00'
              before: '23:00:00'
      sequence:
         ........

Anche in questo caso il choose valuta come prima cosa che alexa_target sia stato passato dal chiamante per procedere ed, in seguito, valuta se l’orario è diurno (9:00 – 23:00) oppure se è stato passato il parametro alexa_force: true.
Se queste condizioni sono soddisfatte proseguiamo.

Richiamo script.multinotify_alexa_pre per ogni dispositivo Alexa
# script.multinotify_pre: salvo volume precedente, imposto volume e metto in pausa eventuale musica su tutti i dispositivi Alexa coinvolti
- variables:
    list: >
      {% set object_id = alexa_target | regex_findall('\\.(\\w+)') | first %}
      {% if not alexa_target is string %}
        {# Elenco di entità inline #}
        {{alexa_target | join(',')}}
      {% elif alexa_target.startswith('group.') %}
        {# Gruppo di Echo definito in Home Assistant #}
        {{expand(states[alexa_target]) | join(',', attribute='entity_id')}}
      {% elif alexa_target == ('media_player.ovunque') or alexa_target.endswith('media_player.everywhere') %}
        {# Tutti gli Echo contemporaneamente #}
        {{expand(states.group.all_alexa) | join(',', attribute='entity_id')}}
      {% elif alexa_target.startswith('media_player.') and states['group.' ~ object_id] != None %}
        {# Gruppo di Echo definito in Alexa (necessario un gruppo Home Assistant corrispondente con lo stesso nome!) #}
        {{expand(states['group.' ~ object_id]) | join(',', attribute='entity_id')}}
      {% elif alexa_target.startswith('media_player.') %}
        {# Singola entità Echo #}
        {{alexa_target}}
      {% endif %}
    count: "{{list.split(',') | count}}"
- repeat:
    while: "{{ repeat.index <= count }}"
    sequence:
      - variables:
          entity_id: "{{list.split(',')[repeat.index-1]}}"
      - service: script.multinotify_alexa_pre
        data:
          target: "{{entity_id}}"
          volume: "{{alexa_volume | default(0.6)}}"

Scopo di questo blocco è quello di richiamare lo script (che analizzeremo in seguito) script.multinotify_alexa_pre per ogni dispositivo Alexa coinvolto nella notifica. Per farlo, come prima cosa, compiliamo una variabile di nome list in cui, a seconda di cosa abbiamo passato se un’entità di gruppo, un gruppo altoparlanti, un elenco di entità o l’elemento ovunque, inseriamo l’elenco delle entità Alexa coinvolte, separate da virgola.
La variabile count contiene invece il numero di entità presenti nella lista (tramite il filtro count).

Preparati questi dati viene eseguito il repeat while come se fosse un ciclo foreach per tutti gli elementi Alexa presenti nella lista preparata sopra. Per simulare un foreach utilizziamo la variabile speciale repeat.index che contiene il numero di ciclo in cui si è. Attenzione che parte da 0 e non da 1 come si potrebbe essere portati a pensare (ecco perchè la condizione di fine è repeat.index <= count).

All’interno del repeat viene impostata la variabile entity_id prelevando l’elemento alla posizione di repeat.index-1 della lista compilata all’inizio e viene passata a script.multinotify_alexa_pre, insieme al volume desiderato.

Lo script multinotify_alexa_pre si occuperà di salvare il volume corrente del dispositivo, mettere in pausa l’eventuale musica salvando che era in riproduzione e infine impostare il volume specificato, più avanti vedremo nel dettaglio come lo fa.

Notifica vera e propria

A questo punto viene richiamato il servizio notify.alexa_media di Alexa Media Player per pronunciare la notifica desiderata:

# Notifica vera e propria
- variables:
    message: "{{alexa_message | default(message)}}"
- service: notify.alexa_media
  data:  
    message: "{{message}}"
    data:
      type: announce
    target: "{{alexa_target}}"
Attesa che Alexa abbia pronunciato la notifica

Una volta avviata la pronuncia questa è asincrona, ovvero restituisce il controllo immediato al chiamante mentre i dispositivi Alexa ne iniziano la pronuncia. Tramite il codice che segue (che mi è costato tanto sudore e tantissime prove empiriche con frasi di ogni tipo!!!) simuliamo il tempo necessario ad Alexa per pronunciare la frase specificata in message così da attendere mentre è in corso la pronuncia:

- delay:
    seconds: "{{(5 + (message.count(', ') + message.count('. ') + message.count('! ') + message.count('? ') + message.count(': ') + message.count('; ')) | float * 0.35 + (message | length) * 0.06) | round(default=0)}}"

La base su cui è calcolato questo tempo è la seguente:

  • 5 secondi fissi sono i tempi morti generali che mediamente Alexa impiega tra avvio e suono di inizio notifica
  • vengono contati i segni di punteggiatura (, . ! ? : 😉 che implicano un aumento del tempo di lettura pari a 0,35 secondi l’uno
  • vengono contati i caratteri della frase da pronunciare, moltiplicati per un tempo medio di 6 centesimi di secondo a carattere

Questi valori li ho ricavati facendo davvero tante prove con frasi corte, lunghe, frastagliate da punteggiatura o prive di punteggiatura. Il risultato è molto aderente al tempo impiegato da Alexa (sebbene sia simulato in quanto non c’è modo di attendere il reale termine della pronuncia).

Unica eccezione non ancora considerata cono le interiezioni come hey che impiegano meno tempo rispetto a quanto calcolato. In una versione seguente calcolerò anche questo.
Richiamo script.multinotify_alexa_post per ogni dispositivo Alexa

Al termine della notifica, similarmente a quanto abbiamo fatto prima della notifica vera e propria, richiamiamo script.multinotify_alexa_post per ogni dispositivo Alexa coinvolto:

# script.multinotify_post: ripristino volume precedente e riproduzione musica, se era in corso, su tutti i dispositivi Alexa coinvolti
- repeat:
    while: "{{ repeat.index <= count }}"
    sequence:
      - variables:
          entity_id: "{{list.split(',')[repeat.index-1]}}"
      - service: script.multinotify_alexa_post
        data:
          target: "{{entity_id}}"

Lo script multinotify_alexa_post si occuperà di ripristinare il volume precedente del dispositivo Alexa e riprendere la musica in riproduzione (se lo era prima dell’annuncio). Più avanti vedremo nel dettaglio come lo fa.

Lo script multinotify_alexa_pre

Come abbiamo visto sopra lo script multinotify_alexa_pre viene richiamato per ogni dispositivo Alexa coinvolto nell’operazione. Vediamo il codice per capirne il funzionamento:

# AD USO INTERNO. Viene chiamato per ogni entità Alexa PRIMA del messaggio di notifica
multinotify_alexa_pre:
  alias: "Gestione singola Alexa PRE"
  description: "Processa la singola Alexa nel ciclo di Multinotify prima dell'annuncio. Non è pensata per l'utilizzo da parte dell'utente"
  fields: 
    target:
      name: "Entità Alexa"
      description: "Entità singola di Alexa da processare"
    volume:
      name: "Volume da impostare"
      description: "Volume da impostare (sarà impostato 0.6 se non specificato)"
  sequence:
    - alias: "Imposto prev volume"
      service: python_script.set_state
      data:
        entity_id: "tmp.alexa_{{ target | regex_findall('\\.(\\w+)') | first }}_prev_volume"
        state: "{{state_attr(target, 'volume_level')}}"
        was_playing: "{{states[target].state == 'playing'}}"
        allow_create: true
    - alias: "Alexa era in riproduzione?"
      choose:
      - conditions: "{{states[target].state == 'playing'}}"
        sequence:
          - alias: "Metto in pausa Alexa"
            service: media_player.media_pause
            target:
              entity_id: "{{target}}"
    - alias: "Imposta volume desiderato"
      service: media_player.volume_set
      target:
        entity_id: "{{target}}"
      data:
        volume_level: "{{volume | default(0.6) | round(2)}}"
    - alias: "Workaround per aggiornare sempre volume_level di Alexa"
      service: python_script.set_state
      data:
        entity_id: "{{target}}"
        volume_level: "{{volume | default(0.6) | round(2)}}"

Lo script ha come parametri di ingresso target, contenente l’entità di Alexa da processare e volume, contenente il volume desiderato per Alexa.

Come prima cosa nella sequence lo script effettua un backup del volume creando un’entità temporanea nominata tmp.alexa_NOME_prev_volume (dove NOME è il nome dell’entità passata senza dominio). Val la pena analizzare bene questo passaggio:

- alias: "Imposto prev volume"
  service: python_script.set_state
  data:
    entity_id: "tmp.alexa_{{ target | regex_findall('\\.(\\w+)') | first }}_prev_volume"
    state: "{{state_attr(target, 'volume_level')}}"
    was_playing: "{{states[target].state == 'playing'}}"
    allow_create: true

Viene utilizzato lo script python set_state per creare o aggiornare l’entità temporanea. Questi i parametri passati a set_state:

  • entity_id. Nome dell’entità da creare. La Regex serve per estrarre dall’entity_id presente in target la sola componente del nome senza dominio. Per esempio per “media_player.mia_alexa” restituirebbe “mia_alexa”. Così viene composto il nome come tmp.alexa_NOME_prev_volume
  • state. Qui memorizziamo semplicemente l’attributo volume_level contenuto nell’entità passata in target.
  • was_playing. In questo attributo memorizziamo true se lo stato dell’entità passata in target era “playing” oppure false in caso contrario
  • allow_create. Viene usato true per specificare che se l’entità non esiste va creata.

A questo punto se il dispositivo Alexa stava riproducendo musica questa viene messa in pausa:

- alias: "Alexa era in riproduzione?"
  choose:
  - conditions: "{{states[target].state == 'playing'}}"
    sequence:
      - alias: "Metto in pausa Alexa"
        service: media_player.media_pause
        target:
          entity_id: "{{target}}"

Questo perchè alzando il volume prima dell’annuncio e abbassandolo dopo l’annuncio è impossibile sincronizzarsi con la musica, risultando in un aumento improvviso del volume della musica che risulta particolarmente sgradevole. In questo modo, invece, tutto avviene correttamente.

Ora viene impostato il volume passato da multinotify (oppure 0.6 se non era stato specificato come parametro):

    - alias: "Imposta volume desiderato"
      service: media_player.volume_set
      target:
        entity_id: "{{target}}"
      data:
        volume_level: "{{volume | default(0.6) | round(2)}}"

Infine viene forzato l’attributo volume_level dell’entità di Alexa al volume impostato, così che sia sempre corrispondente con il reale volume:

- alias: "Workaround per aggiornare sempre volume_level di Alexa"
  service: python_script.set_state
  data:
    entity_id: "{{target}}"
    volume_level: "{{volume | default(0.6) | round(2)}}"

Perchè viene fatta questa operazione? Alexa Media Player si basa su un Websocket costantemente attivo con i server Alexa che aggiorna Home Assistant con gli aggiornamenti di stato dei dispositivi Alexa. Normalmente, quindi, il volume viene aggiornato automaticamente dall’integrazione. Ma capita, purtroppo, che il websocket si disconnetta e l’integrazione non è più in grado di riattivarlo (senza un riavvio di Home Assistant…) e in tal caso il volume non si aggiorna più. Con questo workaround, invece, l’attributo sarà aggiornato sia che il websocket sia in funzione che nel caso contrario.

Avevo aperto un issue chiedendo che questa operazione avvenisse automaticamente nell’integrazione ma l’autore mi ha risposto, in modo non molto garbato, che non ha intenzione di implementarlo. Ecco perchè il workaround…
Lo script multinotify_alexa_post

Al pari dello script multinotify_alexa_pre anche questo script multinotify_alexa_post viene richiamato una volta per ogni dispositivo Alexa coinvolto ma al termine dell’esecuzione di multinotify.

  # AD USO INTERNO. Viene chiamato per ogni entità Alexa DOPO il messaggio di notifica
  multinotify_alexa_post:
    alias: "Gestione singola Alexa POST"
    description: "Processa la singola Alexa nel ciclo di Multinotify dopo l'annuncio. Non è pensata per l'utilizzo da parte dell'utente"
    fields: 
      target:
        name: "Entità Alexa"
        description: "Entità singola di Alexa da processare"
    sequence:
      - variables:
          object_id: "{{target | regex_findall('\\.(\\w+)') | first}}"
          prev_vol: "tmp.alexa_{{object_id}}_prev_volume"
          was_playing: "{{state_attr(prev_vol, 'was_playing')}}"
      - alias: "Imposta volume precedente"
        service: media_player.volume_set
        target:
          entity_id: "{{target}}"
        data:
          volume_level: "{{states(prev_vol)}}"
      - alias: "Workaround per aggiornare sempre volume_level di Alexa"
        service: python_script.set_state
        data:
          entity_id: "{{target}}"
          volume_level: "{{states(prev_vol)}}"

      - alias: "Se era in riproduzione precedentemente ripristino la riproduzione"
        choose:
        - conditions: "{{was_playing}}"
          sequence:
            - alias: "Attesa per lasciare applicare il volume ad Alexa"
              delay: 2
            - alias: "Ripristina riproduzione"
              service: media_player.media_play
              target:
                entity_id: "{{target}}"

Questo script ha come solo parametro di ingresso target contenente, come è facile intuire, l’entity_id del dispositivo Alexa.
La prima cosa che fa questo script è preparare delle variabili da usare in seguito tramite il blocco variables:

- variables:
    object_id: "{{target | regex_findall('\\.(\\w+)') | first}}"
    prev_vol: "tmp.alexa_{{object_id}}_prev_volume"
    was_playing: "{{state_attr(prev_vol, 'was_playing')}}"

In object_id viene messo il nome dell’entity_id senza dominio del dispositivo Alexa. Ad esempio per l’entità media_player.alexa1 object_id conterrà alexa1. La regex, infatti, preleva il contenuto del parametro target a partire dal carattere che segue il punto.

In prev_vol recuperiamo l’entity_id temporanea che avevamo creato dove abbiamo salvato il volume del dispositivo Alexa prima di modificarlo.

In was_playing preleviamo l’attributo was_playing dell’entità temporanea in cui abbiamo salvato se il dispositivo stava riproducendo musica.

Siamo ora pronti a ripristinare il volume precedente all’annuncio:

- alias: "Imposta volume precedente"
  service: media_player.volume_set
  target:
    entity_id: "{{target}}"
  data:
    volume_level: "{{states(prev_vol)}}"

Unica nota vediamo che qui preleviamo lo stato dell’entità il cui entity_id è memorizzato in prev_vol tramite la funzione states.

Come anche nello script precedente anche qui applichiamo il workaround per assicurarci che il volume dell’entità Alexa sia coerente con il volume che abbiamo impostato, anche se il websocket di Alexa Media Player dovesse cadere:

- alias: "Workaround per aggiornare sempre volume_level di Alexa"
  service: python_script.set_state
  data:
    entity_id: "{{target}}"
    volume_level: "{{states(prev_vol)}}"

Ultimo step è quello di riprendere la riproduzione di musica, qualora prima dell’annuncio il dispositivo lo stesse facendo:

- alias: "Se era in riproduzione precedentemente ripristino la riproduzione"
  choose:
  - conditions: "{{was_playing}}"
    sequence:
      - alias: "Attesa per lasciare applicare il volume ad Alexa"
        delay: 2
      - alias: "Ripristina riproduzione"
        service: media_player.media_play
        target:
          entity_id: "{{target}}"

Il choose esegue la sequenza solo se la variabile was_playing è true. In tal caso ci sarà una pausa di 2 secondi, per assicurarci che il cambio volume sia stato eseguito sul dispositivo, e verrà richiamato il servizio media_player.media_play sul dispositivo memorizzato in target. Questo farà riprendere la musica da dove era rimasta.

I workaround per Alexa Media Player

Nello script applico due workaround per degli annosi problemi che affliggono Alexa Media Player e che l’autore dell’integrazione non sembra intenzionato a risolvere.

Il primo lo abbiamo già visto applicato negli script multinotify_alexa_pre e multinotify_alexa_post, ovvero ogni volta che impostiamo un volume su un dispositivo Alexa richiamiamo la seguente azione per assicurarci che l’attributo volume_level dell’entità Alexa rispecchi il volume impostato anche se il websocket di aggiornamento dovesse essersi interrotto (cosa che capita piuttosto frequentemente e non viene più ripristinato fino al riavvio…):

      - alias: "Workaround per aggiornare sempre volume_level di Alexa"
        service: python_script.set_state
        data:
          entity_id: "{{target}}"
          volume_level: "{{states(prev_vol)}}"

Questo l’issue dove l’avevo segnalato all’autore si Alexa Media Player.

Il secondo è rappresentato dall’automazione in fondo al package:

automation:
  # Risolve il bug di attributo volume_level non presente all'avvio di Home Assistant
  # Ref: https://github.com/custom-components/alexa_media_player/issues/1394
  - id: 2d897264-e05c-46bd-ba23-772fbeeab7be
    alias: "Alexa - Evento - Reimposta volume all'avvio di Home Assistant"
    trigger:
      - platform: homeassistant
        event: start
    action:
      - alias: "Imposta il volume al 60%"
        service: media_player.volume_set
        target:
          entity_id: group.all_alexa
        data:
          volume_level: 0.6

Alexa Media Player, quando Home Assistant si avvia, non aggiorna lo stato del volume nelle entità Alexa, ovvero l’attributo volume_level di tali entità non viene valorizzato. Questo non permette a multinotify di conoscere il volume dei dispositivi Alexa, dopo un riavvio di Home Assistant (che come sappiamo è una cosa che capita di frequente).
Di conseguenza al riavvio di Home Assistant imposto il volume di ogni dispositivo Alexa al 60%. Questo fa si che il volume venga impostato negli attributi volume_level e permetta il regolare funzionamento successivo.

Questo l’issue in cui è stato segnalato il problema e io stesso ho dato il mio appoggio.

Conclusione

Spero di non averti annoiato a morte con questo articolo!
Il mio intento è sempre quello di prendere spunto da qualche lavoro per spiegare le tecniche usate e aiutare ad incrementare la capacità di realizzare script ed automazioni sempre più avanzati, piuttosto che la mera diffusione e utilizzo a scatola chiusa, senza capirne i principi di funzionamento.

Se poi non hai avuto tempo o voglia di andare oltre il capitolo “Come funziona lo script multinotify” spero almeno che troverai utile questo package tanto quanto lo trovo utile io! (e chissà che approfondirai il principio di funzionamento un domani)

Una cosa è certa: se sei arrivato fin qui hai tutto il diritto di darti una pacca sulla spalla!

Buone notifiche a tutti 🙂

Ti è piaciuto questo contenuto?

Se ti è stato utile l’articolo che hai letto potresti pensare di darmi una mano offrendomi qualche caffè!
Oltre ad aiutarmi a star sveglio nelle nottate di ricerche e scrittura mi daresti una mano concreta nel sostenere le spese del server web, incentivandomi a produrre altri contenuti di qualità.

Puoi usare Paypal:

Oppure Buymeacoffee:

Henrik Sozzi

Sono un analista programmatore per lavoro e per passione. Amo la domotica, la stampa 3D e la tecnologia in generale.

Questo articolo ha 9 commenti.

  1. Michelangelo

    Bellissimo package, usavo già da tempo la prima versione (modificata) con un Google Chromecast e due Google Nest mini, ora con questa è bello poter reimpostare il volume precedente.
    Mi chiedevo se metterai il supporto ufficiale anche per i Google Nest, con i limiti del caso (ho visto che la musica non viene ripristinata dopo un messaggio e non c’è l’entità media_player.ovunque), che comunque risulta gestibilissimo.
    Eventualmente sono disponibile per fare dei test!

    P.S.: quelle belle icone PNG 48×48 con trasparenza dove le hai trovate?

    1. Henrik Sozzi

      Ciao e grazie! Immagino tu sia l’utente GitHub dell’issue. Rispondo anche qui per completezza: ho appena comprato un Google Nest mini per collaudare e supportare anche Google 😜
      Ho diverse cose in programma per il multinotify in futuro, più servizi di notifica e una centralizzazione che semplificherà notevolmente l’utilizzo. Stay tuned 😉
      PS: le PNG sono prese da varie repository in rete. Alcune sono icone di Windows (r), altre sono di utenti vari e quelle piatte sono mdi icons modificate da me con un glow bianco (non che mi convincano un granché a dire il vero)

  2. Michelangelo

    Si sono stato io 😊
    Su GitHub ti ho allegato il file con le modifiche che ho apportato per farlo funzionare con i Google Nest mini (ho rimesso le entità di esempio originali).
    Non vedo l’ora di vedere gli sviluppi.
    Attendo anche l’articolo aggiornato sulla posizione delle persone, quello mi è stato davvero utile per attivare altre automazioni. 😁

    1. Henrik Sozzi

      Ah ecco 🙂 Ho visto il commento all’issue, grazie per il file! Quando mi arriverà il Google Nest Mini e avrò tempo lo implementerò in modo integrato. Sarà una bell’aggiunta agli altri servizi. Ti ringrazio per la collaborazione e per l’entusiasmo! 🙂
      Riguardo all’articolo sulla posizione devo trovare il tempo di mettermi ad aggiornarlo… Spero di riuscirci presto.

  3. Federico

    Ciao e complimenti per il tuo lavoro!! purtroppo avendo iphone non posso usare appieno le potenzialità di questo fantastico strumento! Attendo con ansia la versione compatibile con app companion ios..

    1. Henrik Sozzi

      Ciao e grazie! Si, mi sono dimenticato di avvisare di questo limite (lo farò appena riesco).
      Ma soprattutto ho intenzione di supportare anche iPhone, Google home ed altri servizi… 😉

  4. Alex

    Ciao Henrik, dopo aver installato il package, quando provo a verificare la configurazione prima del riavvio, Home Assistant mi risponde con “Package multinotify setup failed. Component script has duplicate key ‘alias'” naturalmente non ho modificato nulla sotto la riga “— NON MODIFICARE OLTRE QUESTA RIGA —”
    Puoi darmi qualche dritta?

    1. Henrik Sozzi

      Ciao, può essere che hai il multinotify vecchio in un altro package? Ho scelto di lasciare lo stesso nome quindi o tieni uno o l’altro (questo v2 è MOLTO meglio comunque e molto altro arriverà…)

      1. Alex

        TANA!!! Trovato grazie!

Rispondi