Con Blink succede sempre la stessa cosa: si disarma il sistema per entrare in casa senza farsi riprendere, o per passare un pomeriggio in giardino, e poi ci si dimentica di riarmarlo. Lo si scopre la sera, o il giorno dopo, quando le telecamere sono rimaste spente tutto il tempo.

L'obiettivo di questa guida è togliere la memoria dall'equazione:

  • riarmo automatico dopo un numero di minuti scelto da chi usa il sistema;
  • comandi da WhatsApp, per armare, disarmare o rimandare il riarmo senza aprire l'app;
  • un avviso a ogni disarmo e riarmo, con chi l'ha fatto, e uno se l'armamento non riesce.

Parto dalle telecamere già in Home Assistant, come nella guida Telecamere Blink in Home Assistant. Per la parte WhatsApp servono il gateway con l'elenco dei mittenti autorizzati e i ruoli Admin e Utente con le categorie; senza, il riarmo automatico e la dashboard funzionano lo stesso.

🧠 Come ragiona Blink in Home Assistant

Prima di scrivere una riga di YAML conviene avere chiaro cosa si arma.

  • Si arma il sistema, non la telecamera. Il sistema è il modulo di sincronizzazione, e in Home Assistant è un'entità alarm_control_panel, per esempio alarm_control_panel.blink_casa. È esattamente quello che fa il pulsante dell'app: arma o disarma tutte le telecamere del modulo insieme.
  • Le telecamere hanno un interruttore «rilevamento del movimento», ma da solo non basta: una telecamera registra solo se il suo sistema è armato. Per questa guida conta solo lo stato del sistema.
  • Considera solo i sistemi con telecamere. Ogni entità camera di Blink ha l'attributo sync_module, con il nome del modulo a cui appartiene. Ricavando l'elenco dei sistemi da lì, un vecchio modulo vuoto o offline rimasto nell'account non entra nei conti e non genera un errore a ogni tentativo di armarlo.

⏱️ Il limite da conoscere: Blink si aggiorna ogni 5 minuti

  • L'integrazione Blink interroga il cloud ogni 5 minuti, e l'intervallo non si cambia.
  • Un disarmo fatto dall'app Blink arriva in Home Assistant fino a 5 minuti dopo, e il conteggio dei minuti del riarmo parte da quando Home Assistant lo vede, non da quando hai premuto il pulsante.
  • Il «da quando» è il last_changed dell'entità: dopo un riavvio di Home Assistant riparte da zero, quindi un sistema disarmato riceve di nuovo tutti i minuti di attesa.
  • I comandi dati da Home Assistant (dashboard, WhatsApp, riarmo automatico) invece si vedono subito, perché l'integrazione rilegge lo stato appena eseguito il comando.

🎛️ Le entità di configurazione

Tutto quello che l'utente può cambiare sta in pochi helper, così si modifica dalla dashboard o da WhatsApp senza toccare il YAML.

HelperA cosa serve
input_boolean.blink_riarmo_autoRiarmo automatico acceso o spento
input_number.blink_riarmo_minutiDopo quanti minuti riarmare; 0 vuol dire subito, al primo controllo
input_text.blink_riarmo_programmatoRiarmo una tantum, per esempio «disarma per 60 minuti»: contiene l'ora del riarmo e vale anche con l'automatico spento
input_text.blink_ultimo_comandoL'ultimo comando dato da Home Assistant, nel formato azione|chi|ora: serve per scrivere chi ha armato o disarmato
input_boolean.blink_errore_in_corsoAcceso durante una serie di errori, per non ripetere l'avviso a ogni tentativo
input_text.blink_ultimo_erroreIl motivo dell'ultimo errore, da mostrare in dashboard
input_boolean:
  blink_riarmo_auto:
    name: Blink riarmo automatico
    icon: mdi:shield-refresh
  blink_errore_in_corso:
    name: Blink errore in corso

input_number:
  blink_riarmo_minuti:
    name: Blink riarma dopo (minuti)
    min: 0
    max: 240
    step: 5
    mode: box
    unit_of_measurement: min

input_text:
  blink_riarmo_programmato:
    name: Blink riarmo programmato
    max: 40
  blink_ultimo_comando:
    name: Blink ultimo comando
    max: 255
  blink_ultimo_errore:
    name: Blink ultimo errore
    max: 255

📊 Il sensore con l'elenco dei sistemi

Un template sensor raccoglie in un attributo tutto quello che serve sui sistemi: stato, da quando, stato del modulo, numero di telecamere e ora del prossimo riarmo. Le automazioni e i comandi WhatsApp leggono da qui, invece di ripetere gli stessi calcoli in tre posti.

template:
  - sensor:
      - name: Blink sistemi
        unique_id: blink_sistemi
        state: "{{ this.attributes.sistemi | default([]) | count }}"
        attributes:
          sistemi: >-
            {% set camere = integration_entities('blink') | select('match', 'camera\.') | list %}
            {% set moduli = camere | map('state_attr', 'sync_module') | reject('none') | unique | list %}
            {% set auto = is_state('input_boolean.blink_riarmo_auto', 'on') %}
            {% set minuti = states('input_number.blink_riarmo_minuti') | float(0) | int %}
            {% set prog = as_datetime(states('input_text.blink_riarmo_programmato')) %}
            {% set ns = namespace(l=[]) %}
            {% for m in moduli %}
              {% set p = 'alarm_control_panel.blink_' ~ (m | slugify) %}
              {% set da = states[p].last_changed if states[p] is not none else none %}
              {% set riarmo = '' %}
              {% if is_state(p, 'disarmed') %}
                {% if prog is not none %}{% set riarmo = (prog | as_local).isoformat() %}
                {% elif auto and da is not none %}{% set riarmo = ((da + timedelta(minutes=minuti)) | as_local).isoformat() %}
                {% endif %}
              {% endif %}
              {% set ns.l = ns.l + [{
                'entity': p, 'nome': m, 'stato': states(p),
                'da': da.isoformat() if da is not none else '',
                'modulo': state_attr(p, 'status') | string,
                'camere': camere | map('state_attr', 'sync_module') | select('eq', m) | list | count,
                'riarmo': riarmo }] %}
            {% endfor %}
            {{ ns.l }}

Il nome dell'entità del sistema si ricava dal nome del modulo: il modulo «CASA» diventa alarm_control_panel.blink_casa. Se hai rinominato le entità, sostituisci quella riga con una mappa fissa modulo → entità. Il riarmo programmato, se c'è, vince sull'automatico: chi scrive «disarma per 60 minuti» vuole 60 minuti, non i 30 dell'impostazione generale.

Per la dashboard serve anche un valore unico, l'ora del prossimo riarmo, ed è un secondo sensore:

      - name: Blink prossimo riarmo
        unique_id: blink_prossimo_riarmo
        device_class: timestamp
        state: >-
          {% set l = state_attr('sensor.blink_sistemi', 'sistemi') or [] %}
          {% set orari = l | selectattr('stato', 'eq', 'disarmed') | map(attribute='riarmo') | reject('eq', '') | sort | list %}
          {{ orari[0] if orari | count > 0 else none }}

🪞 Perché un sensore a parte e non lo stato del primo

  • Verrebbe naturale mettere il prossimo riarmo nello stato di sensor.blink_sistemi, calcolandolo da this.attributes.sistemi.
  • Ma in un template sensor this è lo stato precedente dell'entità: lo stato verrebbe calcolato sugli attributi del giro prima, e la dashboard mostrerebbe un orario in ritardo di un aggiornamento (per esempio ancora vuoto subito dopo un disarmo).
  • Il secondo sensore legge l'attributo già aggiornato del primo e si ricalcola quando il primo cambia: il valore è sempre quello giusto. Lo stato del primo, il numero di sistemi, invece non cambia quasi mai, e lì il ritardo non conta.

🔁 L'automazione di riarmo

Parte ogni minuto, cerca i sistemi disarmati con il riarmo scaduto e li passa allo script che arma. Non riprova più di una volta ogni 5 minuti: se il modulo è offline, martellare il cloud Blink ogni minuto non lo fa tornare online prima.

automation:
  - id: blink_riarmo_automatico
    alias: Blink - riarmo automatico
    mode: single
    triggers:
      - trigger: time_pattern
        minutes: "/1"
    variables:
      scaduti: >-
        {% set l = state_attr('sensor.blink_sistemi', 'sistemi') or [] %}
        {% set ns = namespace(e=[]) %}
        {% for s in l if s.stato == 'disarmed' and s.riarmo != '' and as_datetime(s.riarmo) <= now() %}
          {% set ns.e = ns.e + [s.entity] %}
        {% endfor %}
        {{ ns.e }}
    conditions:
      - "{{ scaduti | count > 0 }}"
      # al massimo un tentativo ogni 5 minuti
      - >-
        {% set t = state_attr('script.blink_arma', 'last_triggered') %}
        {{ t is none or now() - t > timedelta(minutes=5) }}
    actions:
      - action: input_text.set_value
        target:
          entity_id: input_text.blink_ultimo_comando
        data:
          value: "arma|riarmo automatico|{{ now().isoformat() }}"
      - action: script.blink_arma
        data:
          sistemi: "{{ scaduti }}"

Il controllo dei 5 minuti usa il last_triggered dello script, che Home Assistant tiene già: nessun helper in più. E sta direttamente nella condizione, non in una variabile, perché nelle variables la data diventerebbe un testo e la sottrazione con now() darebbe errore.

🔒 Lo script che arma e verifica

Chiamare alarm_arm_away non basta: il comando può partire e Blink non confermare, perché il modulo è offline o il cloud non risponde. Lo script quindi arma, aspetta la conferma, e se non arriva capisce perché.

  1. Chiama alarm_control_panel.alarm_arm_away con continue_on_error: true, così un errore del cloud non ferma lo script.
  2. Aspetta con wait_template fino a 45 secondi che tutti i sistemi siano armed_away.
  3. Se non arriva, forza una rilettura con homeassistant.update_entity (senza aspettare il giro dei 5 minuti) e aspetta altri 30 secondi.
  4. Se ancora niente, ricava il motivo e lo salva.
  5. Manda l'avviso d'errore solo se non è già in corso una serie di errori.
script:
  blink_arma:
    alias: Blink - arma
    mode: single
    max_exceeded: silent
    fields:
      sistemi:
        description: Elenco dei sistemi da armare (vuoto = tutti)
    sequence:
      - variables:
          lista: >-
            {% set tutti = (state_attr('sensor.blink_sistemi', 'sistemi') or []) | map(attribute='entity') | list %}
            {{ sistemi if sistemi is defined and sistemi | count > 0 else tutti }}
      - action: alarm_control_panel.alarm_arm_away
        continue_on_error: true
        target:
          entity_id: "{{ lista }}"
      - wait_template: "{{ lista | reject('is_state', 'armed_away') | list | count == 0 }}"
        timeout: "00:00:45"
      - if: "{{ not wait.completed }}"
        then:
          - action: homeassistant.update_entity
            continue_on_error: true
            target:
              entity_id: "{{ lista }}"
          - wait_template: "{{ lista | reject('is_state', 'armed_away') | list | count == 0 }}"
            timeout: "00:00:30"
      - variables:
          mancanti: "{{ lista | reject('is_state', 'armed_away') | list }}"
      - if: "{{ mancanti | count > 0 }}"
        then:
          - variables:
              motivo: >-
                {% set p = mancanti[0] %}
                {% if states(p) in ['unavailable', 'unknown'] %}integrazione non disponibile
                {% elif (state_attr(p, 'status') | string | lower) == 'offline' %}modulo offline
                {% else %}Blink non ha confermato{% endif %}
          - action: input_text.set_value
            target:
              entity_id: input_text.blink_ultimo_errore
            data:
              value: "{{ motivo | trim }} ({{ now().strftime('%H:%M') }})"
          - if: "{{ is_state('input_boolean.blink_errore_in_corso', 'off') }}"
            then:
              - action: input_boolean.turn_on
                target:
                  entity_id: input_boolean.blink_errore_in_corso
              - action: script.avviso_categoria
                data:
                  categoria: telecamere
                  testo: |-
                    *⚠️ BLINK NON ARMATO*
                    ━━━━━━━━━━━━━━
                    Motivo: {{ motivo | trim }}

                    _Riprovo ogni 5 minuti, ti avviso quando riesce_

script.avviso_categoria sta per lo script che già manda un messaggio agli Admin e agli Utenti iscritti a una categoria, quello della guida sui ruoli. Lo script non spegne l'errore quando l'armamento riesce: lo fa l'automazione degli avvisi qui sotto, che così può scrivere «errore risolto» nel messaggio di riarmo.

🔔 Un avviso a ogni cambio di stato

L'avviso non parte dai comandi ma dal cambio di stato del sistema, quindi arriva qualunque sia la fonte: WhatsApp, riarmo automatico, dashboard o app Blink. Tre regole:

  • Ignorare i passaggi da e verso unavailable. A ogni riavvio di Home Assistant, o quando il cloud Blink non risponde, il sistema passa da unavailable a armed_away: senza questo filtro arriverebbe un falso «Blink riarmato» ogni volta.
  • Scrivere chi è stato, in quest'ordine: il comando registrato in blink_ultimo_comando negli ultimi 3 minuti, se è la stessa azione (WhatsApp con il nome, oppure riarmo automatico); poi il context.user_id del nuovo stato, che c'è quando il comando arriva dalla dashboard di Home Assistant; altrimenti «app Blink o altra fonte».
  • Dire cosa succede dopo: l'avviso di disarmo dice a che ora avverrà il riarmo, o che il riarmo automatico è spento; quello di riarmo dice da quanto il sistema era disarmato.
automation:
  - id: blink_avviso_stato
    alias: Blink - avviso cambio stato
    mode: queued
    triggers:
      - trigger: state
        entity_id: alarm_control_panel.blink_casa
    conditions:
      - "{{ trigger.from_state is not none and trigger.to_state is not none }}"
      - "{{ trigger.from_state.state not in ['unavailable', 'unknown'] }}"
      - "{{ trigger.to_state.state in ['armed_away', 'disarmed'] }}"
    variables:
      armato: "{{ trigger.to_state.state == 'armed_away' }}"
      sistema: "{{ trigger.to_state.name }}"
      chi: >-
        {% set c = states('input_text.blink_ultimo_comando').split('|') %}
        {% set azione = 'arma' if trigger.to_state.state == 'armed_away' else 'disarma' %}
        {% set quando = as_datetime(c[2]) if c | count == 3 else none %}
        {% set uid = trigger.to_state.context.user_id %}
        {% if quando is not none and c[0] == azione and now() - quando < timedelta(minutes=3) %}{{ c[1] }}
        {% elif uid %}dashboard ({{ states.person | selectattr('attributes.user_id', 'eq', uid) | map(attribute='name') | first | default('utente di Home Assistant') }})
        {% else %}app Blink o altra fonte{% endif %}
      durata: >-
        {% set m = ((now() - trigger.from_state.last_changed).total_seconds() / 60) | int %}
        {{ m ~ ' min' if m < 60 else (m // 60) ~ ' h ' ~ (m % 60) ~ ' min' }}
      riarmo: >-
        {% set prog = as_datetime(states('input_text.blink_riarmo_programmato')) %}
        {% if prog is not none %}alle {{ (prog | as_local).strftime('%H:%M') }}
        {% elif is_state('input_boolean.blink_riarmo_auto', 'on') %}alle {{ ((trigger.to_state.last_changed + timedelta(minutes=states('input_number.blink_riarmo_minuti') | float(0))) | as_local).strftime('%H:%M') }}
        {% else %}spento{% endif %}
      risolto: "{{ is_state('input_boolean.blink_errore_in_corso', 'on') }}"
    actions:
      - if: "{{ armato }}"
        then:
          - action: script.avviso_categoria
            data:
              categoria: telecamere
              testo: |-
                *🔒 BLINK RIARMATO*
                ━━━━━━━━━━━━━━
                📹 Sistema: {{ sistema }}
                👤 Da: {{ chi | trim }}
                ⏱️ Era disarmato da {{ durata | trim }}{% if risolto %}
                ✅ Errore risolto{% endif %}
          - action: input_boolean.turn_off
            target:
              entity_id: input_boolean.blink_errore_in_corso
          - action: input_text.set_value
            target:
              entity_id: input_text.blink_riarmo_programmato
            data:
              value: ""
        else:
          - action: script.avviso_categoria
            data:
              categoria: telecamere
              testo: |-
                *🔓 BLINK DISARMATO*
                ━━━━━━━━━━━━━━
                📹 Sistema: {{ sistema }}
                👤 Da: {{ chi | trim }}
                🔁 Riarmo: {{ riarmo | trim }}

                _Scrivi blink arma per riarmarlo prima_

Due dettagli. L'ora del riarmo nell'avviso è calcolata qui dentro e non letta da sensor.blink_prossimo_riarmo: il sensore si aggiorna sullo stesso cambio di stato, e non c'è garanzia che lo faccia prima dell'automazione. E al riarmo il riarmo programmato si svuota, così una vecchia ora non resta lì a valere per il disarmo successivo.

Su WhatsApp l'avviso di riarmo cita il messaggio del disarmo, così nella chat si vede a colpo d'occhio quale disarmo è stato chiuso. Serve un gateway che restituisca l'id del messaggio inviato e accetti un messaggio in risposta: si salva l'id dell'avviso di disarmo in un helper di testo e lo si passa all'avviso di riarmo. Se il tuo gateway non lo permette, il resto funziona uguale.

🔓 BLINK DISARMATO ━━━━━━━━━━━━━━ 📹 Sistema: CASA 👤 Da: WhatsApp (Admin) 🔁 Riarmo: alle 15:40 Scrivi blink arma per riarmarlo prima15:10
🔒 BLINK RIARMATO ━━━━━━━━━━━━━━ 📹 Sistema: CASA 👤 Da: riarmo automatico ⏱️ Era disarmato da 30 min15:40
⚠️ BLINK NON ARMATO ━━━━━━━━━━━━━━ Motivo: modulo offline Riprovo ogni 5 minuti, ti avviso quando riesce22:05

Esempio: disarmo da WhatsApp, riarmo automatico dopo 30 minuti e un errore

💬 I comandi WhatsApp

MessaggioCosa faChi può
blinkStato di ogni sistema, da quando, prossimo riarmoAdmin e Utenti con Telecamere
blink armaArma tutti i sistemiAdmin e Utenti con Telecamere
blink disarmaDisarma; il riarmo segue l'impostazione automaticaAdmin e Utenti con Telecamere
blink disarma 60Disarma e riarma fra 60 minuti, anche con l'automatico spentoAdmin e Utenti con Telecamere
blink auto on · blink auto offAccende o spegne il riarmo automaticoSolo Admin
blink minuti 30Imposta dopo quanti minuti riarmareSolo Admin

I permessi seguono la stessa logica degli altri comandi di casa. I comandi base appartengono alla categoria 📹 Telecamere: li usano gli Admin e gli Utenti che hanno quella categoria accesa. Le impostazioni (auto e minuti) cambiano il comportamento per tutti, quindi sono solo per gli Admin. E aiuto mostra a ognuno solo i comandi che può usare: un Utente con Telecamere vede le righe di Blink senza auto e minuti, un Utente senza la categoria non le vede affatto.

blink18:02
📹 BLINK ━━━━━━━━━━━━━━ 🔓 CASA: disarmato da 12 min 📷 Telecamere: 4 · modulo online 🔁 Riarmo alle 18:20 Scrivi blink arma per riarmarlo adesso18:02
blink minuti 1518:03
🔒 COMANDO RISERVATO ━━━━━━━━━━━━━━ Le impostazioni del riarmo sono riservate agli Admin. Puoi usare blink, blink arma e blink disarma18:03

Esempio: un Utente con la categoria Telecamere chiede lo stato e prova a cambiare i minuti

Gli avvisi seguono la stessa logica: arrivano agli Admin e agli Utenti iscritti a Telecamere, ed è per questo che tutti passano da script.avviso_categoria con categoria: telecamere.

L'automazione che riceve il webhook del gateway calcola già ruolo e nome del mittente per gli altri comandi. Per Blink controlla il permesso sulla categoria e passa ruolo e nome allo script, che parte con script.turn_on per non tenere il webhook in attesa:

- conditions: "{{ (testo | string | lower | trim).split(' ')[0] == 'blink' }}"
  sequence:
    - if: "{{ not (ruolo == 'admin' or 'telecamere' in categorie) }}"
      then:
        - action: script.whatsapp_rispondi
          data:
            chat: "{{ chat }}"
            testo: |-
              *🔒 COMANDO NON DISPONIBILE*
              ━━━━━━━━━━━━━━
              Il tuo profilo non comprende le telecamere.
      else:
        - action: script.turn_on
          target:
            entity_id: script.blink_comando
          data:
            variables:
              testo: "{{ testo }}"
              chat: "{{ chat }}"
              nome: "{{ nome }}"
              admin: "{{ ruolo == 'admin' }}"

E lo script dei comandi, ridotto ai rami principali. script.whatsapp_rispondi sta per lo script con cui già rispondi a una chat:

script:
  blink_comando:
    alias: Blink - comando WhatsApp
    mode: queued
    fields:
      testo: {}
      chat: {}
      nome: {}
      admin: {}
    sequence:
      - variables:
          parole: "{{ (testo | string | lower | trim).split()[1:] }}"
          azione: "{{ parole[0] if parole | count > 0 else 'stato' }}"
          valore: "{{ parole[1] if parole | count > 1 else '' }}"
          is_admin: "{{ admin | string | lower in ['true', '1', 'on'] }}"
          sistemi: "{{ (state_attr('sensor.blink_sistemi', 'sistemi') or []) | map(attribute='entity') | list }}"
      - choose:
          # PRIMA il controllo dei permessi, poi i rami che eseguono
          - conditions: "{{ azione in ['auto', 'minuti'] and not is_admin }}"
            sequence:
              - action: script.whatsapp_rispondi
                data:
                  chat: "{{ chat }}"
                  testo: |-
                    *🔒 COMANDO RISERVATO*
                    ━━━━━━━━━━━━━━
                    Le impostazioni del riarmo sono riservate agli Admin.

          - conditions: "{{ azione == 'arma' }}"
            sequence:
              - action: input_text.set_value
                target:
                  entity_id: input_text.blink_ultimo_comando
                data:
                  value: "arma|WhatsApp ({{ nome }})|{{ now().isoformat() }}"
              - action: script.blink_arma
                data:
                  sistemi: "{{ sistemi }}"

          - conditions: "{{ azione == 'disarma' }}"
            sequence:
              # prima il riarmo programmato, poi il disarmo: l'avviso lo legge subito
              - action: input_text.set_value
                target:
                  entity_id: input_text.blink_riarmo_programmato
                data:
                  value: >-
                    {{ (now() + timedelta(minutes=valore | string | int)).isoformat()
                       if (valore | string).isdigit() else '' }}
              - action: input_text.set_value
                target:
                  entity_id: input_text.blink_ultimo_comando
                data:
                  value: "disarma|WhatsApp ({{ nome }})|{{ now().isoformat() }}"
              - action: alarm_control_panel.alarm_disarm
                continue_on_error: true
                target:
                  entity_id: "{{ sistemi }}"

          - conditions: "{{ azione == 'auto' and (valore | string) in ['on', 'off'] }}"
            sequence:
              - action: "input_boolean.turn_{{ valore }}"
                target:
                  entity_id: input_boolean.blink_riarmo_auto
              - action: script.whatsapp_rispondi
                data:
                  chat: "{{ chat }}"
                  testo: |-
                    *🔁 RIARMO AUTOMATICO*
                    ━━━━━━━━━━━━━━
                    {{ '🟢 Acceso' if (valore | string) == 'on' else '⚫ Spento' }}

          - conditions: "{{ azione == 'minuti' and (valore | string).isdigit() }}"
            sequence:
              - action: input_number.set_value
                target:
                  entity_id: input_number.blink_riarmo_minuti
                data:
                  value: "{{ valore | string | int }}"
              - action: script.whatsapp_rispondi
                data:
                  chat: "{{ chat }}"
                  testo: |-
                    *🔁 RIARMO AUTOMATICO*
                    ━━━━━━━━━━━━━━
                    ⏱️ Riarmo dopo {{ valore }} minuti

A blink arma e blink disarma lo script non risponde con un messaggio suo: la conferma è l'avviso 🔒 o 🔓 che arriva a tutti, mittente compreso, e dice anche chi è stato. Il ramo dello stato (solo blink) legge sensor.blink_sistemi e scrive una riga per sistema, come nel mockup sopra.

🖥️ La dashboard

Una scheda sola per usare e configurare il riarmo: stato, sistema, interruttore del riarmo, minuti, prossimo riarmo, ultimo errore e un pulsante per armare subito. I minuti compaiono solo con il riarmo acceso, l'ultimo errore solo se c'è un errore in corso: due righe conditional della scheda entità.

📹 Blink
StatoDisarmato
Modulo CASAonline
Riarmo automatico
Riarma dopo (minuti)30 min
Prossimo riarmo15:40
Arma adessoARMA

Esempio: la scheda con il riarmo acceso e nessun errore in corso

type: entities
title: 📹 Blink
entities:
  - entity: alarm_control_panel.blink_casa
    name: Stato
  - type: attribute
    entity: alarm_control_panel.blink_casa
    attribute: status
    name: Modulo CASA
  - entity: input_boolean.blink_riarmo_auto
    name: Riarmo automatico
  - type: conditional
    conditions:
      - entity: input_boolean.blink_riarmo_auto
        state: "on"
    row:
      entity: input_number.blink_riarmo_minuti
      name: Riarma dopo (minuti)
  - entity: sensor.blink_prossimo_riarmo
    name: Prossimo riarmo
    format: time
  - type: conditional
    conditions:
      - entity: input_boolean.blink_errore_in_corso
        state: "on"
    row:
      entity: input_text.blink_ultimo_errore
      name: Ultimo errore
  - type: button
    name: Arma adesso
    icon: mdi:shield-lock
    action_name: ARMA
    tap_action:
      action: perform-action
      perform_action: script.blink_arma

Il pulsante chiama lo script senza dati, quindi arma tutti i sistemi; nell'avviso comparirà «dashboard» con il nome della persona, grazie al context.user_id. La scelta dei canali di ogni avviso (WhatsApp, app, email) non sta qui ma in una vista a parte, la stessa degli altri avvisi di casa, come nella guida sui messaggi su WhatsApp, email e app: questa scheda resta corta e si usa al volo.

🪤 Le trappole dei template

Gli script sono quasi tutti template Jinja, e tre trappole fanno perdere più tempo di tutto il resto.

1. Nelle variables il testo cambia tipo

Home Assistant converte il risultato di un template nelle variables nel suo tipo «naturale»: «1» diventa il numero 1, «true» diventa il booleano True. Un confronto con un testo allora non è mai vero, e i metodi dei testi danno errore.

# con "blink minuti 30" valore è il numero 30
# Sbagliato: isdigit() su un numero ferma lo script
- conditions: "{{ valore.isdigit() }}"
# Sbagliato: 30 non è uguale a '30'
- conditions: "{{ valore == '30' }}"

# Giusto: | string nel punto in cui lo usi
- conditions: "{{ (valore | string).isdigit() }}"
- conditions: "{{ (valore | string) == '30' }}"

2. Il booleano che arriva come testo

L'automazione passa admin: "{{ ruolo == 'admin' }}", cioè True o False. Ma a seconda di come viene chiamato, lo script può riceverlo come booleano o come testo «True», «true», «on». {{ not admin }} su un testo non vuoto è sempre falso, quindi un Utente risulterebbe Admin. Va normalizzato una volta, all'inizio:

- variables:
    # Sbagliato: "False" come testo è vero
    # is_admin: "{{ admin }}"
    is_admin: "{{ admin | string | lower in ['true', '1', 'on'] }}"

3. Nel choose vince la prima condizione vera

Un choose esegue il primo ramo con la condizione vera e ignora gli altri. Se il ramo «comando riservato agli Admin» sta dopo il ramo che esegue blink minuti, un Utente cambia i minuti e il controllo non viene mai raggiunto. Il controllo dei permessi va sempre per primo, come nello script sopra:

- choose:
    # 1. prima il rifiuto
    - conditions: "{{ azione in ['auto', 'minuti'] and not is_admin }}"
      sequence: [...]
    # 2. poi i rami che eseguono
    - conditions: "{{ azione == 'minuti' and (valore | string).isdigit() }}"
      sequence: [...]

🧪 Come provarlo

  1. Accendi il riarmo automatico e metti i minuti a 5, dalla dashboard o con blink auto on e blink minuti 5.
  2. Scrivi blink disarma: deve arrivare l'avviso 🔓 con il tuo nome e l'ora del riarmo, 5 minuti più tardi.
  3. Scrivi blink: lo stato deve dire «disarmato da» e la stessa ora di riarmo.
  4. Aspetta: entro un minuto dall'ora indicata arriva l'avviso 🔒 «riarmo automatico», con «era disarmato da 5 min».
  5. Prova blink disarma 10 con l'automatico spento: il riarmo deve arrivare comunque, dopo 10 minuti.
  6. Disarma dall'app Blink: l'avviso arriva fino a 5 minuti dopo e dice «app Blink o altra fonte».
  7. Dal numero di un Utente con Telecamere prova blink minuti 15: deve rispondere «comando riservato».
  8. Rimetti i minuti che vuoi davvero.

✅ Checklist finale

  • I sistemi in sensor.blink_sistemi sono solo quelli con telecamere, e i nomi delle entità corrispondono.
  • Con 0 minuti il riarmo avviene al primo controllo dopo il disarmo, cioè entro un minuto da quando Home Assistant lo vede.
  • Un riavvio di Home Assistant non manda avvisi di riarmo falsi.
  • Con il modulo offline arriva un solo avviso ⚠️, e al primo armamento riuscito il 🔒 dice «errore risolto».
  • Gli avvisi arrivano agli Admin e agli Utenti con Telecamere, e a nessun altro.
  • L'aiuto di un Utente non mostra auto e minuti.
  • Il riarmo programmato si svuota dopo il riarmo.

💡 Idee per estenderlo

  • Armare solo di notte: una condizione d'orario nell'automazione di riarmo, o un secondo valore di minuti per le ore notturne.
  • Disarmare quando il telefono arriva a casa: un'automazione sulla zona casa dell'entità person che disarma e scrive «arrivo a casa» in blink_ultimo_comando, così l'avviso dice anche perché.
  • Un riepilogo serale: a un'ora fissa un messaggio con lo stato di ogni sistema e quante volte è stato disarmato durante il giorno, utile per accorgersi di un sistema rimasto spento.