Da WhatsApp posso chiedere la foto di una telecamera: scrivo telecamere, arriva un elenco numerato, rispondo con il numero e Home Assistant mi manda uno scatto. Lo racconto nella guida sulle telecamere su WhatsApp.
Un giorno, chiedendo la foto di una Arlo, è arrivata un'immagine del giorno prima, presentata come se fosse di adesso. Il messaggio riportava l'ora della richiesta, non quella dello scatto, e niente faceva sospettare che qualcosa non andasse. Una telecamera che ti mostra il passato spacciandolo per il presente è peggio di una telecamera spenta: almeno quella sai che non funziona.
Il mio impianto: Home Assistant in Docker su un mini PC Windows, le Arlo collegate con il componente personalizzato aarlo installato da HACS, e qualche Blink con l'integrazione ufficiale.
🔍 La diagnosi: due cause, una dentro l'altra
Causa 1: la libreria non dà errore, ti dà la foto vecchia
Per avere uno scatto uso il servizio aarlo.camera_request_snapshot_to_file: sveglia la telecamera, aspetta l'immagine e la scrive in un file. Il punto debole è cosa fa quando l'immagine non arriva. Se lo scatto non arriva entro circa 60 secondi, la libreria scrive comunque nel file l'ultima immagine che ha in memoria, senza nessun errore.
Dall'esterno quindi sembra tutto andato a buon fine: il servizio termina, il file c'è, lo script prosegue e WhatsApp consegna la foto. Solo che la foto è vecchia.
Causa 2: la base Arlo era offline da ore
La vera origine era a monte: la base station Arlo era offline da ore. Senza la base le telecamere non possono scattare, quindi ogni richiesta finiva nel caso della causa 1.
Nessuno se n'era accorto perché le entità telecamera in Home Assistant restavano disponibili: per Home Assistant c'erano, avevano un'immagine, avevano i loro attributi. Il problema non era nella telecamera, ma nell'anello prima.
La soluzione ha tre parti: controllare la data della foto prima di mandarla, dirlo chiaramente nel messaggio quando è vecchia, e avvisare quando la base va offline.
⏱️ Parte 1 · controllo di freschezza prima di inviare
L'entità camera di aarlo ha un attributo image_source che dice da dove viene l'immagine attuale e quando è stata fatta:
snapshot/10-27 15:54
capture/10-27 15:54
Il formato è tipo/MM-GG HH:MM: snapshot è uno scatto chiesto, capture un'immagine da una registrazione. Due dettagli contano: l'orario è locale e manca l'anno.
Lo script fa tre cose:
- segna l'ora della richiesta;
- chiede lo scatto, fino a 2 tentativi;
- confronta: la foto è nuova solo se l'orario di
image_sourceè successivo alla richiesta, con 90 secondi di tolleranza. La tolleranza serve perché l'orario ha solo ore e minuti: uno scatto delle 15:54:50 risulta «15:54», cioè prima di una richiesta delle 15:54:20.
Il template Jinja che legge la data
{% set src = state_attr('camera.arlo_cam1', 'image_source') | string %}
{# "snapshot/10-27 15:54" → "10-27 15:54" #}
{% set quando = src.split('/')[-1] %}
{# aggiungo l'anno; con none come default un valore strano non manda in errore il template #}
{% set t = strptime(now().year ~ '-' ~ quando, '%Y-%m-%d %H:%M', none) %}
{# l'orario è locale: as_local gli dà il fuso di Home Assistant #}
{{ t is not none and as_timestamp(t | as_local) >= richiesta - 90 }}
split('/')[-1]prende la parte dopo la barra, qualunque sia il tipo.strptime(..., none)restituiscenoneinvece di un errore se l'attributo manca o ha un formato diverso: in quel caso la foto conta come non nuova, che è la scelta prudente.as_localdà al valore il fuso di Home Assistant;as_timestamplo trasforma in secondi, confrontabili con l'ora della richiesta.
L'anno aggiunto è quello corrente: a cavallo di Capodanno una foto del 31 dicembre letta il 1° gennaio finirebbe nell'anno nuovo, cioè nel futuro, e passerebbe il controllo. Se ti preoccupa, togli un anno quando la data risulta più avanti di adesso.
Lo script, semplificato
script:
foto_arlo:
alias: Foto Arlo con controllo di freschezza
mode: parallel
fields:
camera:
example: camera.arlo_cam1
sequence:
- variables:
richiesta: "{{ now().timestamp() }}"
file: "/config/www/scatti/{{ camera.split('.')[1] }}.jpg"
- repeat:
sequence:
- action: aarlo.camera_request_snapshot_to_file
continue_on_error: true
data:
entity_id: "{{ camera }}"
filename: "{{ file }}"
# al massimo 2 tentativi; si esce prima se la foto è nuova
until: >-
{% set t = strptime(now().year ~ '-' ~ (state_attr(camera, 'image_source') | string).split('/')[-1], '%Y-%m-%d %H:%M', none) %}
{{ repeat.index >= 2 or (t is not none and as_timestamp(t | as_local) >= richiesta - 90) }}
- variables:
scatto: >-
{% set t = strptime(now().year ~ '-' ~ (state_attr(camera, 'image_source') | string).split('/')[-1], '%Y-%m-%d %H:%M', none) %}
{{ (t | as_local).isoformat() if t is not none else '' }}
fresca: "{{ scatto != '' and as_timestamp(scatto) >= richiesta - 90 }}"
Il template è ripetuto nell'until e nelle variables finali perché il primo va rivalutato a ogni giro, dopo ogni tentativo. Le variabili scatto e fresca servono al messaggio, nella parte 2.
Lo stesso principio per le Blink
Le Blink non hanno image_source, ma la miniatura ha un URL che cambia a ogni scatto nuovo. Lo script salva l'URL prima della richiesta e lo confronta con quello dopo: se è uguale, la foto non è nuova. L'orario dello scatto si ricava dal parametro ts= dell'URL, che è un timestamp:
{% set ts = url_dopo | regex_findall('ts=(\\d+)') %}
{{ (ts[0] | int) | timestamp_custom('%d/%m alle %H:%M') if ts else 'sconosciuto' }}
🔴 Parte 2 · un avviso ben visibile nel messaggio
Se la foto non è nuova la mando lo stesso: un'immagine vecchia con la sua data è comunque un'informazione. Ma in fondo al messaggio compare un blocco in grassetto che non si può non vedere.
testo: |-
*📷 {{ nome_telecamera }}*
━━━━━━━━━━━━━━
🕐 Scatto: {{ as_timestamp(scatto) | timestamp_custom('%d/%m alle %H:%M') if scatto else 'sconosciuto' }}{% if not fresca %}
🔴🔴 *FOTO NON AGGIORNATA* 🔴🔴
*La telecamera non ha mandato una foto nuova: questa è l'ultima disponibile, del {{ as_timestamp(scatto) | timestamp_custom('%d/%m alle %H:%M') if scatto else 'giorno sconosciuto' }}.*{% endif %}
Anche la riga dell'ora cambia: adesso è l'ora dello scatto, non quella della richiesta. È lì che stava l'inganno del vecchio messaggio.
🎨 Perché le emoji e non il testo rosso
- WhatsApp non supporta i colori nel testo. La formattazione disponibile è grassetto (
*testo*), corsivo (_testo_), barrato (~testo~) e monospazio (tre apici inversi). - Il «rosso» si ottiene con le emoji 🔴: due per lato attirano l'occhio anche scorrendo la chat di fretta.
- Le altre regole di formattazione sono nella guida sulle notifiche WhatsApp formattate.
Prima e dopo, sulla stessa richiesta con la base offline:
Prima: l'ora è quella della richiesta, la foto sembra di adesso
Dopo: l'ora dello scatto e l'avviso in fondo
Lo stesso controllo vale per gli avvisi automatici di movimento delle Arlo, quelli della guida sul movimento in casa. Lì non c'è un'ora di richiesta, quindi il confronto è con adesso: una foto più vecchia di 10 minuti riceve la stessa etichetta.
📡 Parte 3 · un avviso quando la base Arlo va offline
La parte 2 dice che la foto è vecchia, ma non perché. Per la causa vera serve un avviso sulla base. aarlo crea per la base un binary_sensor di connessione, qui binary_sensor.aarlo_connected_base: on se è collegata, off se no.
Perché un controllo periodico e non un trigger di stato
La scelta naturale sarebbe un trigger «passa a off da 10 minuti». Ma se la base è già offline quando Home Assistant si riavvia, lo stato non cambia: parte già off, e un trigger di stato non scatterebbe mai. È proprio il caso in cui l'avviso serve di più.
Per questo uso un trigger time_pattern ogni 5 minuti con la condizione «off da almeno 10 minuti». Ogni 5 minuti l'automazione guarda com'è la base, e non dipende da aver visto il cambio.
Niente doppioni: l'helper «già avvisata»
Un controllo ogni 5 minuti, da solo, manderebbe un avviso ogni 5 minuti. Un input_boolean «già avvisata» lo impedisce:
- l'avviso di offline parte solo se è spento, e lo accende;
- l'avviso «tornata online» (base
onda 2 minuti) parte solo se è acceso, e lo spegne: niente «tornata online» senza un offline prima; - gli
input_booleanmantengono lo stato dopo i riavvii, quindi un riavvio non fa ripartire l'avviso.
I canali si scelgono da un input_select in dashboard (WhatsApp, app, email), come gli altri avvisi della guida su WhatsApp, email e app. Qui script.invia_avviso sta per lo script che già usi per mandare un avviso sui canali scelti.
automation:
- id: base_arlo_offline
alias: Base Arlo - offline
mode: single
triggers:
- trigger: time_pattern
minutes: "/5"
conditions:
- condition: state
entity_id: binary_sensor.aarlo_connected_base
state: "off"
for: "00:10:00"
- condition: state
entity_id: input_boolean.base_arlo_avvisata
state: "off"
actions:
- action: input_boolean.turn_on
target:
entity_id: input_boolean.base_arlo_avvisata
- action: script.invia_avviso
data:
canali: "{{ states('input_select.avviso_base_arlo') }}"
# il gateway restituisce l'id del messaggio: lo salvo per la risposta
salva_id_in: input_text.base_arlo_msg_offline
testo: |-
*📡 BASE ARLO OFFLINE*
━━━━━━━━━━━━━━
Le telecamere Arlo non possono scattare.
_Le foto che arrivano sono le ultime in memoria_
- id: base_arlo_online
alias: Base Arlo - tornata online
mode: single
triggers:
- trigger: time_pattern
minutes: "/5"
conditions:
- condition: state
entity_id: binary_sensor.aarlo_connected_base
state: "on"
for: "00:02:00"
- condition: state
entity_id: input_boolean.base_arlo_avvisata
state: "on"
actions:
- action: input_boolean.turn_off
target:
entity_id: input_boolean.base_arlo_avvisata
- action: script.invia_avviso
data:
canali: "{{ states('input_select.avviso_base_arlo') }}"
# risposta citando l'avviso di offline
rispondi_a: "{{ states('input_text.base_arlo_msg_offline') }}"
testo: |-
*✅ BASE ARLO TORNATA ONLINE*
━━━━━━━━━━━━━━
Le telecamere possono di nuovo scattare.
Il for della condizione si basa sull'ultimo cambio di stato, che dopo un riavvio riparte da zero: con la base già offline, l'avviso arriva 10 minuti dopo il riavvio. Su WhatsApp il messaggio di ritorno cita quello di offline, così nella chat si vede la coppia e quanto è durato il problema. Serve un gateway che restituisca l'id del messaggio inviato e accetti una risposta; se il tuo non lo fa, togli salva_id_in e rispondi_a e il resto funziona uguale.
Esempio: l'avviso di offline e la risposta quando la base torna
💡 Le lezioni
📌 Tre cose che valgono anche fuori da Arlo
- «Nessun errore» non vuol dire «dato nuovo». Quando una libreria ha una cache, va controllata la data del dato, non l'esito della chiamata.
- Un'entità «disponibile» non garantisce che il dispositivo funzioni. Conviene monitorare anche l'anello a monte: base, hub, gateway.
- I template nelle variables convertono i risultati. In Home Assistant un testo come «1» diventa un numero, e un confronto con «1» non è più vero. Nei confronti usa
| string, e per le datestrptimecon un valore di default.
Per la configurazione di base delle telecamere c'è la guida Le telecamere Arlo in Home Assistant; per le Blink, Telecamere Blink in Home Assistant.