Powiadomienie „brama garażowa jest otwarta od 20 minut” jest przydatne. Jeszcze lepiej, gdy pod nim jest przycisk Zamknij, bo sprawę da się załatwić z ekranu blokady, bez otwierania panelu. Aplikacja Home Assistant Companion na iOS i Androida obsługuje właśnie takie przyciski. Po dotknięciu przycisku telefon wysyła do Home Assistanta zdarzenie, a automatyzacja, która na nie czeka, robi resztę.

Sam YAML z przyciskami jest krótki. Więcej pracy wymaga to, co dzieje się za nimi: żeby dotknięcie starego powiadomienia nie uruchomiło niewłaściwej automatyzacji, żeby przebieg, na który nikt nie odpowiedział, nie blokował następnego, i żeby nieaktualne powiadomienia znikały z telefonu.

Co będzie potrzebne

  • Aplikacja Home Assistant Companion zainstalowana i zalogowana na każdym telefonie, z uprawnieniem do powiadomień przyznanym podczas konfiguracji.
  • Akcja powiadomień dla każdego telefonu. Aplikacja rejestruje ją jako notify.mobile_app_ z identyfikatorem urządzenia na końcu. Domyślnie identyfikator to nazwa telefonu z ustawień systemu, w której spacje i inne znaki spoza liter i cyfr zastąpiono podkreśleniami. Dokładny identyfikator pokazuje menu aplikacji Companion w ustawieniach Home Assistanta.

We wszystkich przykładach poniżej występuje notify.mobile_app_your_phone. Zamień to na własną nazwę.

Najprostsze powiadomienie z przyciskami

Przyciski trafiają na listę actions w wewnętrznym bloku data:

action: notify.mobile_app_your_phone
data:
  title: "Brama garażowa"
  message: "Brama garażowa jest otwarta od 20 minut."
  data:
    actions:
      - action: "CLOSE_GARAGE"
        title: "Zamknij"
      - action: "IGNORE_GARAGE"
        title: "Zostaw otwartą"

action to identyfikator, który Home Assistant dostanie po dotknięciu przycisku, a title to napis na przycisku. Gdy ktoś wybierze Zamknij, Home Assistant wywoła zdarzenie mobile_app_notification_action z action: CLOSE_GARAGE w danych. Nic więcej się nie stanie, dopóki jakaś automatyzacja na to zdarzenie nie zareaguje.

Liczbę przycisków ogranicza telefon. Android pokazuje najwyżej trzy. iOS przyjmie więcej, ale według dokumentacji Companion przy około dziesięciu systemowy interfejs zaczyna mieć problemy z przewijaniem. Jeśli ktokolwiek w domu ma Androida, planuj trzy przyciski.

Prosta obsługa dotknięcia

Najszybciej jest napisać osobną automatyzację, która nasłuchuje konkretnego identyfikatora:

alias: "Garaż: zamknij z powiadomienia"
triggers:
  - trigger: event
    event_type: mobile_app_notification_action
    event_data:
      action: "CLOSE_GARAGE"
actions:
  - action: cover.close_cover
    target:
      entity_id: cover.garage_door

To działa. Dla przycisku, który zawsze znaczy to samo, na przykład wspólnego WYCISZ pod każdym powiadomieniem alarmowym, to właściwy wzorzec. Dokumentacja nazywa taki wyzwalacz ogólnym („catch-all”).

Ma jedną słabość. Identyfikator jest wspólny dla wszystkich powiadomień, które go kiedykolwiek użyły. Jeśli na telefonie wisi jeszcze wczorajsze powiadomienie o bramie i ktoś dziś dotknie w nim Zamknij, automatyzacja i tak zamknie bramę. Przy bramie albo zamku do drzwi to może być przykra niespodzianka.

Niepowtarzalne identyfikatory i czekanie na odpowiedź

Dokumentacja Companion zaleca budowanie identyfikatorów z identyfikatora kontekstu bieżącego przebiegu, tak żeby każde powiadomienie miało własne przyciski, a potem czekanie na odpowiedź w tej samej automatyzacji. Wygląda to tak:

alias: "Garaż: zapytaj o zamknięcie otwartej bramy"
mode: parallel
triggers:
  - trigger: state
    entity_id: cover.garage_door
    to: "open"
    for: "00:20:00"
actions:
  - variables:
      action_close: "{{ 'CLOSE_' ~ context.id }}"
      action_ignore: "{{ 'IGNORE_' ~ context.id }}"
      notification_tag: "garage-open"
  - action: notify.mobile_app_your_phone
    data:
      title: "Brama garażowa"
      message: "Brama garażowa jest otwarta od 20 minut."
      data:
        tag: "{{ notification_tag }}"
        actions:
          - action: "{{ action_close }}"
            title: "Zamknij"
          - action: "{{ action_ignore }}"
            title: "Zostaw otwartą"
  - wait_for_trigger:
      - trigger: event
        event_type: mobile_app_notification_action
        event_data:
          action: "{{ action_close }}"
      - trigger: event
        event_type: mobile_app_notification_action
        event_data:
          action: "{{ action_ignore }}"
      - trigger: state
        entity_id: cover.garage_door
        to: "closed"
    timeout: "01:00:00"
  - if:
      - "{{ wait.trigger is not none and wait.trigger.platform == 'event' and wait.trigger.event.data.action == action_close }}"
    then:
      - action: cover.close_cover
        target:
          entity_id: cover.garage_door
  - action: notify.mobile_app_your_phone
    data:
      message: "clear_notification"
      data:
        tag: "{{ notification_tag }}"

Teraz po kolei.

Identyfikatory. context.id jest inny przy każdym przebiegu, więc CLOSE_ z tym identyfikatorem pasuje tylko do przycisków tego jednego powiadomienia. Dotknięcie starszego powiadomienia wysyła identyfikator, na który nikt nie czeka, i nic się nie dzieje.

Czekanie. wait_for_trigger wstrzymuje przebieg, aż zadziała jeden z wyzwalaczy. Kończą je trzy rzeczy: któryś z dwóch przycisków albo zamknięcie bramy w inny sposób (pilotem, przyciskiem na ścianie, z panelu). Bez trzeciego wyzwalacza przebieg czekałby dalej na odpowiedź, która przestała mieć sens.

Limit czasu. Jeśli nikt nie odpowie w ciągu godziny, czekanie i tak się skończy. continue_on_timeout ma domyślnie wartość true, więc przebieg idzie dalej, wait.trigger ma wartość none, a if niczego nie robi. Sprawdzenie najpierw wait.trigger is not none chroni szablon przed błędem w tym wariancie.

Sprzątanie. Ostatni krok wysyła specjalną wiadomość clear_notification z tym samym tag, co usuwa powiadomienie z telefonu niezależnie od tego, co zakończyło czekanie. Dokumentacja ostrzega, że usunięcie może wymagać niedawnego użycia aplikacji: dotyczy to wszystkich powiadomień na iOS i tych na Androidzie, które nie są oznaczone jako krytyczne. Nie traktuj tego jako gwarancji.

Tryb automatyzacji wybierz świadomie

Automatyzacja czekająca na odpowiedź działa przez cały czas czekania. To ma znaczenie przy wyborze trybu:

  • single (domyślny): jeśli wyzwalacz zadziała ponownie w trakcie czekania, nowy przebieg się nie zacznie, a Home Assistant zapisze w dzienniku ostrzeżenie. Przy „brama otwarta od 20 minut” zwykle to nie przeszkadza, bo otwarta brama nie otworzy się drugi raz.
  • restart: nowy wyzwalacz przerywa czekający przebieg. Przyciski z pierwszego powiadomienia wskazują wtedy przebieg, którego już nie ma, i ich dotknięcie nic nie da.
  • parallel albo queued: każdy wyzwalacz dostaje własny przebieg. Domyślny limit to 10 przebiegów. Dokumentacja Companion proponuje tryb równoległy razem z limitami czasu i usuwaniem powiadomień, tak jak w przykładzie wyżej.

Jest jeszcze jedno ograniczenie. Ponowne uruchomienie Home Assistanta przerywa wszystkie czekające automatyzacje i po starcie one nie wracają. Powiadomienie wysłane przed aktualizacją albo restartem może dalej wisieć na telefonie, ale jego przyciski prowadzą donikąd. Jeśli przycisk steruje czymś ważnym, wyślij pytanie jeszcze raz po starcie albo dodaj to samo sterowanie do panelu.

Przyciski, które coś otwierają

Przycisk może też otworzyć stronę. Ustaw action na URI i dodaj uri:

actions:
  - action: "URI"
    title: "Pokaż kamery"
    uri: "/lovelace/cameras"

Ścieżka względna otwiera widok w Home Assistancie: /lovelace/sciezka-widoku dla domyślnego panelu albo /adres-panelu/sciezka-widoku dla innego. Na Androidzie entityId:light.kitchen otwiera okno szczegółów encji, app:// z nazwą pakietu uruchamia inną aplikację, a deep-link://tel:... otwiera wybieranie numeru. Na iOS działają zwykłe adresy, takie jak tel: i https://. Na Androidzie żadna z tych opcji nie zadziała, jeśli action nie ma dokładnie wartości URI.

O tym, co się stanie po dotknięciu samego powiadomienia, a nie przycisku, decydują klucze url (iOS) i clickAction (Android) w wewnętrznym bloku data.

Zabezpieczenia przy wrażliwych przyciskach

Każdy, kto widzi ekran blokady, może dotknąć przycisku. Przy Odblokuj albo Rozbrój to ma znaczenie.

actions:
  - action: "{{ action_unlock }}"
    title: "Odblokuj"
    authenticationRequired: true
  - action: "{{ action_ignore }}"
    title: "Ignoruj"
    destructive: true

authenticationRequired: true oznacza, że telefon trzeba odblokować, zanim akcja się wykona. Na Androidzie wymaga to Androida 12 lub nowszego, iOS obsługuje to we wszystkich wersjach. destructive: true działa tylko na iOS i pokazuje napis na czerwono. Przydaje się jako ostrzeżenie przy przyciskach w rodzaju Włącz syrenę.

Można też sprawdzić, kto odpowiedział. Kontekst zdarzenia zawiera user_id, więc w szablonie wait.trigger.event.context.user_id mówi, który użytkownik Home Assistanta dotknął przycisku. To przydatne w dzienniku albo do zignorowania odpowiedzi od kogoś, kto nie powinien otwierać drzwi wejściowych.

Odpowiedź tekstowa

Identyfikator REPLY albo dodanie behavior: textInput do dowolnego przycisku sprawia, że telefon poprosi o tekst. Tekst przychodzi w zdarzeniu jako reply_text:

actions:
  - action: "{{ action_note }}"
    title: "Dodaj notatkę"
    behavior: textInput

Po czekaniu wait.trigger.event.data.reply_text zawiera wpisany tekst. Wygodne na przykład do dopisania czegoś do listy zakupów bez otwierania aplikacji.

Wysyłanie na kilka telefonów

Każdy telefon ma własną akcję powiadomień. Żeby zapytać cały dom, wywołaj kilka akcji po kolei albo zdefiniuj w configuration.yaml grupę powiadomień, tak jak w dokumentacji Companion:

notify:
  - name: all_phones
    platform: group
    services:
      - action: mobile_app_phone_one
      - action: mobile_app_phone_two

Przy niepowtarzalnych identyfikatorach wygrywa pierwsza osoba, która dotknie przycisku, bo czekanie kończy się na pierwszym zdarzeniu. Krok z clear_notification musi wtedy trafić na każdy telefon, więc wyślij go przez tę samą grupę.

Zastępowanie i grupowanie powiadomień

Dwa kolejne klucze utrzymują porządek na liście powiadomień:

  • tag zastępuje wcześniejsze powiadomienie z tym samym znacznikiem zamiast dokładać kolejne. Czujnik, który co chwilę zgłasza „nadal otwarte”, pokazuje wtedy jedno aktualne powiadomienie. iOS nie zastępuje powiadomień krytycznych.
  • group zbiera powiązane powiadomienia w jeden stos. Dokumentacja odradza używanie tego samego tag w różnych grupach.

Na Androidzie timeout w wewnętrznym bloku data sam usuwa powiadomienie po podanej liczbie sekund. To prosty sposób, żeby stare pytania nie wisiały na telefonie, gdy limit czasu w automatyzacji jest krótszy niż czas, po którym ktoś zwykle zerka na telefon.

Testy przed pierwszym użyciem

  1. Wyślij powiadomienie z Ustawienia > Narzędzia > Akcje (Settings > Tools > Actions) i sprawdź, czy przyciski pojawiają się na każdym telefonie.
  2. Otwórz Ustawienia > Narzędzia > Zdarzenia (Settings > Tools > Events), zacznij nasłuchiwać mobile_app_notification_action, dotknij przycisku i obejrzyj dane zdarzenia. Tam widać dokładnie, do czego musi pasować wyzwalacz.
  3. Uruchom całą automatyzację i w śladzie sprawdź każdą ścieżkę: dotknięty przycisk, brama zamknięta ręcznie i upływ limitu czasu. Wszystkie trzy powinny kończyć się usunięciem powiadomienia.
  4. Po zakończeniu przebiegu dotknij przycisku w starym powiadomieniu. Nic nie powinno się stać.