Una lista sola, aggiornata da più parti
La lista della spesa di casa esiste già su Google Keep, ed è lì che voglio che resti. Quello che mi mancava era poterla usare dal dashboard di Home Assistant e, soprattutto, da WhatsApp: scrivo «spesa latte, pane» e l'articolo compare in lista. Ho collegato le tre cose con un'integrazione e due pezzi di YAML.
L'integrazione: Google Keep Sync, fissata a un commit
Google Keep non offre un'integrazione ufficiale in Home Assistant. Uso Google Keep Sync, che è un'integrazione personalizzata non ufficiale: il suo progetto dichiara di non essere affiliato né approvato da Google. Sincronizza in entrambe le direzioni le liste che scegli e le espone come liste todo di Home Assistant. Si appoggia a due librerie Python, gpsoauth e gkeepapi, che Home Assistant scarica al primo avvio.
Trattandosi di codice di terzi che gira dentro casa mia, non installo «l'ultima versione»: uno script scarica da GitHub il codice di un commit preciso, quello che ho controllato, e lo installa in custom_components. Prima di copiarlo verifica tre cose:
- che nel pacchetto ci sia il
manifest.jsonatteso e che il dominio siagoogle_keep_sync; - che le librerie richieste siano soltanto quelle che mi aspetto (
gpsoauth==2.0.0egkeepapi==0.17.1): se il commit ne chiede altre, si ferma; - che il download abbia un'impronta SHA256 leggibile a video, per poterla confrontare.
Se esiste già una versione installata, la rinomina con la data invece di sovrascriverla, e scrive il commit in un file COMMIT.txt dentro la cartella. Per aggiornare in futuro provo la versione nuova con calma e cambio l'argomento -Commit di proposito.
# parametro -Commit: hash completo di 40 caratteri, non un tag né "main"
$url = "https://codeload.github.com/watkins-matt/home-assistant-google-keep-sync/zip/$Commit"
Invoke-WebRequest -Uri $url -OutFile $zip -UseBasicParsing -TimeoutSec 120
Write-Host "SHA256: $((Get-FileHash $zip -Algorithm SHA256).Hash)"
$m = Get-Content "$comp\manifest.json" -Raw | ConvertFrom-Json
if ($m.domain -ne 'google_keep_sync') { throw "Dominio inatteso: $($m.domain)" }
$expected = 'gpsoauth==2.0.0', 'gkeepapi==0.17.1'
foreach ($r in $m.requirements) {
if ($expected -notcontains $r) { throw "Dipendenza inattesa: $r" }
}
Lo script non tocca nessuna credenziale. L'email e il token li inserisci tu, dopo il riavvio, dalla pagina dell'integrazione (Impostazioni, Dispositivi e servizi, Aggiungi integrazione, «Google Keep Sync»), scegliendo quale lista sincronizzare. Il token non va mai incollato in chat o in un file condiviso.
La lista nella dashboard
Quando la lista di Keep compare in Home Assistant, ha un'entità todo. Un secondo script la cerca da solo nel registro delle entità (quelle create da google_keep_sync): se ne trova una la usa, se ne trova più d'una ti chiede qual è la spesa. Poi aggiunge la scheda «Spesa» con tre pezzi:
- la card della lista, per aggiungere e spuntare gli articoli;
- un riquadro «Come funziona» con la spiegazione dei comandi;
- il pulsante Sincronizza adesso con Keep, che chiama
google_keep_sync.request_sync.
La sincronizzazione ha due velocità. Quello che scrivo in Home Assistant arriva subito su Keep. Quello che cambio su Keep arriva in Home Assistant ogni 15 minuti, oppure subito se premo il pulsante.
I comandi da WhatsApp
Ai comandi che il gateway già capisce (descritti nella guida sui messaggi) ne ho aggiunti tre, e li ho inseriti anche nel messaggio di aiuto.
spesa latte, pane, uova: aggiunge gli articoli. Si separano con virgole o andando a capo. Ancheaggiungi ...funziona.lista(ospesada sola): mostra gli articoli ancora da comprare.togli latte: toglie un articolo.
Il cuore del comando è una manciata di righe: spezzo il testo sulle virgole e sugli a capo, ripulisco gli spazi e aggiungo ogni voce con il servizio todo.add_item. La risposta passa dallo script notifica, quindi conta nel tetto giornaliero di messaggi come ogni altro.
- variables:
articoli: >-
{%- set resto = testo.split(None, 1)[1] if (testo.split(None, 1) | length) > 1 else '' -%}
{{ resto.replace('\n', ',').replace(';', ',').split(',') | map('trim') | reject('eq', '') | list }}
- if: "{{ articoli | length > 0 }}"
then:
- repeat:
for_each: "{{ articoli }}"
sequence:
- action: todo.add_item
target:
entity_id: todo.NOME_DELLA_TUA_LISTA
data:
item: "{{ repeat.item | capitalize }}"
Per la lista uso il servizio todo.get_items con lo stato needs_action, cioè gli articoli non ancora spuntati.
Quando è l'inchiostro a finire
La lista non serve solo alle mie dita. L'avviso sui materiali della stampante può aggiungere da solo alla lista della spesa una riga «Cartuccia ...», senza creare duplicati: lo racconto nella guida sulla stampante.