In casa la lista della spesa è una nota di Google Keep che si chiama SPESA. La vedo sul telefono, la vede chi fa la spesa, e Home Assistant la legge e la scrive tramite un'integrazione HACS. Il problema era arrivarci da tutte le parti: un messaggio WhatsApp, una frase detta ad Alexa o a Google, un vocale mandato mentre guido.
Alla fine funzionano tutti e quattro i canali. Ci sono arrivato passando per due vicoli ciechi e un errore mio, che racconto perché sono la parte utile.
Prima di tutto, guardare cosa c'è già
Non volevo costruire niente prima di sapere dove finisce ogni comando. Ho fatto tre prove semplici: «spesa uova» su WhatsApp, «Ok Google, aggiungi latte alla lista della spesa», «Alexa, aggiungi pane alla lista della spesa». Poi ho controllato le liste.
- WhatsApp arrivava già in SPESA: Home Assistant riceve il messaggio dal gateway OpenWA e aggiunge la voce.
- Google scriveva direttamente in SPESA, perché nell'app Google Home Keep è impostato come fornitore delle liste. Niente da fare.
- Alexa salva nella sua lista della spesa. Home Assistant la vede, tramite l'integrazione Alexa Devices, come un'entità to-do che si aggiorna ogni cinque minuti.
Alexa: un travaso, e un errore di lettura
Per Alexa ho scritto un'automazione che fa da travaso. Quando nella lista di Alexa compare una voce, la copia in SPESA se non c'è già (il confronto ignora maiuscole e spazi), la toglie da Alexa e manda una conferma su WhatsApp.
- action: todo.get_items
target:
entity_id:
- todo.lista_spesa_alexa
- todo.google_keep_spesa
data:
status: needs_action
response_variable: liste
Il resto è un ciclo che aggiunge in Keep le voci nuove e poi le rimuove da Alexa per uid, così la lista di Alexa resta una casella di arrivo.
Qui ho sbagliato. Dopo l'installazione le voci passavano da Alexa a Keep, ma la traccia dell'automazione diceva «condizione falsa, lista vuota». Ho concluso che esistesse un collegamento esterno, magari configurato negli account Amazon o Google, che spostava le voci al posto mio, e ho spento il travaso. Poi è arrivata la conferma su WhatsApp, che manda solo il mio travaso.
Il registro attività di Home Assistant ha chiarito tutto: le voci le aveva spostate proprio l'automazione. La traccia che leggevo era l'esecuzione successiva, partita perché lo spostamento aveva cambiato la lista. Da allora per capire chi ha fatto cosa guardo il registro e tutte le tracce, non solo l'ultima.
I vocali: il gateway non scaricava l'audio
L'idea era semplice: mando un vocale al numero di casa, Home Assistant lo trascrive e lo tratta come un comando scritto. Il primo vocale di prova è arrivato a Home Assistant come messaggio di tipo voice, con un campo media che diceva omitted: true. L'audio non c'era.
Nel log di OpenWA la riga era esplicita: il download del media in ingresso era fallito. Era lo stesso difetto che pochi giorni prima mi impediva di mandare le foto delle telecamere: il motore che usavo, whatsapp-web.js, guida un browser su WhatsApp Web, e con la versione attuale di WhatsApp Web non riusciva più a scaricare né a caricare file. Il testo funzionava, i media no.
OpenWA però include un secondo motore, Baileys, che parla direttamente con i server di WhatsApp senza browser. Il motore è unico per tutta l'istanza, quindi per provarlo senza toccare il gateway in uso ho avviato un secondo container di prova, con la stessa immagine, dati separati e il motore impostato via variabile d'ambiente:
environment:
ENGINE_TYPE: baileys
CHAT_MEDIA_ARCHIVE_ENABLED: "true"
L'ho collegato allo stesso numero come dispositivo in più. Con Baileys il vocale e la foto arrivavano interi e si scaricavano dall'API; da Baileys partivano anche un testo con citazione e una foto con didascalia. A quel punto ho fatto il backup dei dati del gateway vero, ho cambiato motore e ricollegato il numero con un QR. La sessione ha mantenuto lo stesso identificativo, quindi webhook e configurazione di Home Assistant sono rimasti com'erano.
Un dettaglio che mi ha fatto perdere qualche minuto: il primo vocale ricevuto dalla sessione di prova non si scaricava. Era il vocale vecchio, arrivato con la sincronizzazione della cronologia al momento del collegamento: per quelli WhatsApp non passa il file. Con un vocale nuovo, tutto a posto.
Whisper in locale
Per la trascrizione uso Whisper in un container accanto a Home Assistant, con l'immagine rhasspy/wyoming-whisper, la stessa dell'add-on ufficiale. Il modello è small-int8 in italiano. L'audio non esce dal mini PC, e su un Ryzen 3 a basso consumo una frase di 4,3 secondi viene trascritta in 2,6.
Uno script Python, chiamato da Home Assistant come shell_command, fa tre cose: scarica il vocale dal gateway, lo converte con ffmpeg in PCM a 16 kHz e lo manda a Whisper con la libreria wyoming, che nel container di Home Assistant c'è già.
async with AsyncTcpClient("whisper", 10300) as client:
await client.write_event(Transcribe(language="it").event())
await client.write_event(AudioStart(rate=16000, width=2, channels=1).event())
# ... i pezzi di audio ...
await client.write_event(AudioStop().event())
Capire cosa si chiede
La trascrizione è una frase, non un comando. Lo stesso script la riconduce a tre azioni: aggiungere, leggere la lista, togliere. Toglie le formule di contorno («aggiungi», «ricordami di comprare», «alla lista della spesa»), divide sulle virgole e sulla «e», e toglie gli articoli.
Qui i test mi hanno salvato. Alla prima versione «Aggiungi latte e pane» diventava «Tte» e «Pane»: l'espressione che toglieva l'articolo «la» mangiava l'inizio di «latte», perché non chiedeva uno spazio dopo l'articolo. L'ho scoperto solo perché, prima di collegare WhatsApp, ho fatto girare lo script su una decina di frasi scritte a mano:
Aggiungi latte e pane alla lista della spesa -> spesa [Latte, Pane]
Metti il prosciutto cotto e due mozzarelle -> spesa [Prosciutto cotto, Due mozzarelle]
Togli il latte dalla lista -> togli [Latte]
Leggimi la lista -> lista
La risposta su WhatsApp comincia sempre con quello che è stato capito, così un errore di trascrizione si vede subito:
🎙️ Ho capito: «Aggiungi latte e pane alla lista della spesa.»
🛒 AGGIUNTO ALLA SPESA
➕ Latte
➕ Pane
In sintesi
- Prima di costruire, una prova per canale: metà del lavoro era già fatto.
- Il registro attività dice chi ha fatto cosa; l'ultima traccia può raccontare l'esecuzione sbagliata.
- Se un gateway WhatsApp non gestisce i media, il problema può essere il motore e non la configurazione.
- Una modifica rischiosa si prova in un container separato, con un backup pronto.
- Un parser di frasi si prova su frasi vere prima di collegarlo a qualcosa.