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:

  1. segna l'ora della richiesta;
  2. chiede lo scatto, fino a 2 tentativi;
  3. 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) restituisce none invece 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_local dà al valore il fuso di Home Assistant; as_timestamp lo 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:

209:12
📷 foto di ieri
📷 INGRESSO ━━━━━━━━━━━━━━ 🕐 28/10 alle 09:1209:13

Prima: l'ora è quella della richiesta, la foto sembra di adesso

209:12
📷 foto di ieri
📷 INGRESSO ━━━━━━━━━━━━━━ 🕐 Scatto: 27/10 alle 15:54 🔴🔴 FOTO NON AGGIORNATA 🔴🔴 La telecamera non ha mandato una foto nuova: questa è l'ultima disponibile, del 27/10 alle 15:54.09:14

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 on da 2 minuti) parte solo se è acceso, e lo spegne: niente «tornata online» senza un offline prima;
  • gli input_boolean mantengono 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.

📡 BASE ARLO OFFLINE ━━━━━━━━━━━━━━ Le telecamere Arlo non possono scattare. Le foto che arrivano sono le ultime in memoria07:40
✅ BASE ARLO TORNATA ONLINE ━━━━━━━━━━━━━━ Le telecamere possono di nuovo scattare.10:05

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 date strptime con 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.