Bonjour à tous,
Dans l’idée d’améliorer la communication de la maison vers les utilisateurs, j’ai conçu un script HA qui retient les notifications jusqu’à retour en zone de la personne ou de l’appareil mobile.
C’est librement inspiré de ce qu’a fait Horizon Domotique sous AppDeamon : https://www.youtube.com/watch?v=chJylIK0ASo
C’est une solution clé en main qui peut être utilisé comme la centrale de toutes vos notifications. Cela devrait vous décharger de toutes cette partie là dans vos automates et vos scripts
Les notifications peuvent être habillées (icônes, couleurs, illustration) et jusqu’à trois boutons action peuvent être ajoutée.
Le script prend en charge simultanément 6 personnes / appareils / user_id avec une zone de surveillance personnalisable pour chaque appel du script
J’ai préféré le format script HA pour pouvoir le personnaliser depuis un téléphone sans logiciel, matériel ou connaissance en développement.
Ce script propose de multiple paramétrage grâce aux champs. L’idée est d’exploiter tout le potentiel des notifications sans utiliser de code qui très bien documenté sur Introduction | Home Assistant Companion Docs
L’usage du champ persistant peut être amélioré avec l’automate Renvoyer une notification persistante cela vous permettra de créer de véritable notification persistante avec gestion des réponses utilisateurs.
Listes des champs avec leurs explications
- personnes : Qui notifier ? Personnes ou appareil mobile. Vide pour notifier tous les appareils… pensez à personnaliser le choix proposé par défaut
- prox_zone : Zone à surveiller. Si vide, pas de détection retour zone
- prox_distance : Distance pour l’entrée en zone de surveillance. Si vide, pas de détection retour zone
- timeout : Durée de retention des notifications. 18h si vide ou omis
- titre : Titre utilisé pour la notification
- message : Corp du message, possible d’utiliser du HTML
- resume : Texte court affiché quand la notification n’est pas déroulée
- tag : Pour remplacer une notification ayant le même tag
- icone : Icône illustrant la notification
- couleur : Couleur de la notification
- illustration : Illustration placée à droite de la notification
- collant : Rester affiché après ouverture de la notification
- persistant : Force l’affichage, doit être effacé par une autre notification. Ne fonctionne plus depuis Android 14 et doit être renvoyé par un automate
- click : Action suite au clic
- log : Ajouter une notification persistante dans HA
- actions : Mode avancé : 3 actions de notification maximum
- source : Informations sur le déclencheur. Code à adapter… généralement celui proposé fonctionne presque partout
Precisions :
- Si personnes est vide, tous les appareils notifiable sont automatiquement pris en compte. Il est possible de passer la valeur d’un user_id qui serait communiqué par un automate ou un autre script.
- timeout permet d’affiner la durée de rétention. Parce que savoir en rentrant du travail 12h après l’évènement « la cuisson du four est terminé » n’est pas pertinent (normalement un autre habitant est là sur place). Dans ce cas 1h suffit. Pour une machine à laver 18h ou plus de délais permet de couvrir une période d’absence lié à une activité professionnelle conventionnelle.
- illustration doit être accessible sur le web ou dans le dossier /media/local/
- persistant peut être renvoyer avec un automate en utilisant un tag…
- actions vous pouvez indiquer à HA que l’action demandé est faite… avec un automate dédié vous pouvez effacer la notification chez tous les utilisateurs ou uniquement sur celui qui a répondu… une démo couvrant cette usage est proposée
- log : envoi un notification persistante dans HA, permet d’avoir un log simplifié dans la zone de notification de HA… si source est activé, ce sera affiché à cet emplacement
- source : permet de remonter la source de la notification, utile pour améliorer ses scripts et automate… fonctionne si log est activé
Voici le code du script. En dehors des lignes 626 et 627, il n’y a pas de personnalisation à effectuer pour le faire fonctionner immédiatement.
J’ai pris le temps d’harmoniser la structure du code et de placer des labels à toutes les étapes.
Cela fait deux mois que je n’ai plus apporté d’amélioration. Je le considère comme stable et fiable pour couvrir 95% des usages.
Code du script Notification multi-utilisateur avec présence
alias: Notification multi-utilisateur avec présence
sequence:
- alias: Définition de CIBLE
variables:
cible: |-
{# Si vide, extraction de tout les mobile_app #}
{% if personnes == '' or personnes is not defined %}
{{ integration_entities('mobile_app')
| select('match','device_tracker')
| list }}
{% else %}
{# Convertion chaine en liste #}
{% if personnes.entity_id is string %}
{% set personnes = { 'entity_id': [personnes.entity_id]} %}
{% endif %}
{# Récupération des person et device_tracker en liste #}
{% set non_id = personnes.entity_id | select('match', 'person|device_tracker') | list %}
{# Conversion des usr_id en person #}
{% set id = personnes.entity_id | reject('search', '\.') | list %}
{% set id_people = states.person
| selectattr('attributes.user_id', 'in', id)
| map(attribute='entity_id') | list %}
{# Définition liste de person et device_tracker sans doublon #}
{{ (non_id + id_people) | unique | list }}
{% endif %}
- alias: Définition de TIMEOUT à 18h par défaut
variables:
timeout: |-
{% if timeout == '' or timeout is not defined %}
18:00:00
{% else %}
{{timeout}}
{% endif %}
- alias: Notif persistante si LOG
if:
- alias: LOG est activé
condition: template
value_template: "{{ log == true }}"
then:
- alias: Gestion TAG pour notification persistante HA
if:
- alias: Si un TAG existe, écrasement de la notification persistante
condition: template
value_template: "{{ tag is defined }}"
then:
- alias: Effacement si clear_notification
if:
- alias: Message est clear_notification
condition: template
value_template: "{{ message == \"clear_notification\" }} "
then:
- alias: Effacement notification persistante
service: persistent_notification.dismiss
metadata: {}
data:
notification_id: "{{tag}}"
else:
- alias: "Notification persistante dans HA avec écrasement "
service: notify.persistent_notification
data:
title: "{{titre}}"
message: >-
{{ now().strftime('%H:%M:%S')
}}<br>{{message}}<br><br>*Source: {% if source is defined
%}{{ source }}{% elif this is defined %}{{
this.attributes.friendly_name }}{% else %}déclenchement
manuel{% endif %}*
data:
notification_id: "{{tag}}"
else:
- alias: "Notification persistante dans HA sans écrasement "
service: notify.persistent_notification
data:
title: "{{titre}}"
message: >-
{{ now().strftime('%H:%M:%S') }}<br>{{message}}<br><br>*Source:
{% if source is defined %}{{ source }}{% elif this is defined
%}{{ this.attributes.friendly_name }}{% else %}déclenchement
manuel{% endif %}*
- alias: Notif mobile vers CIBLE présente
parallel:
- alias: Entrée n°1 de CIBLE
if:
- alias: Si entrée n°1 de CIBLE existe
condition: template
value_template: "{{ cible[0] is defined }}"
then:
- alias: Variables Personne/Appareil et Zone
variables:
cible_tmp: "{{cible[0]}}"
prox_zone: "{% if prox_zone is defined %}{{prox_zone.entity_id}}{% endif %}"
- alias: Attendre si hors zone
if:
- alias: La CIBLE est à plus de xx mètres de la zone
condition: template
value_template: >-
{% if prox_zone is defined and prox_distance is defined %}
{{ (distance( prox_zone, cible_tmp ) * 1000) | int >
prox_distance | float(0) }}
{% endif %}
then:
- alias: Attendre arrivée dans la zone de surveillance
wait_for_trigger:
- alias: Lorsque CIBLE est à moins de xx mètres de la zone
platform: template
value_template: >-
{{ (distance( prox_zone, cible_tmp ) * 1000) | int <
prox_distance | float(0) }}
timeout: "{{timeout}}"
continue_on_timeout: false
- alias: Identification de l'appareil mobile à notifier
variables:
device_name: >-
{# Conversion de person en mobile_app #}
{% if 'person.' in cible_tmp %}
{{ expand(cible_tmp)
| map(attribute='attributes.source')
| list
| join
| replace('device_tracker.', 'mobile_app_') }}
{# Conversion de device_tracker avec GPS en mobile_app #}
{% elif 'device_tracker.' in cible_tmp and
(state_attr(cible_tmp,'gps_accuracy') or 0 ) > 0 %}
mobile_app_{{ state_attr(cible_tmp,'friendly_name') | slugify }}
{# Déjà un mobile_app #}
{% elif 'mobile_app_' in cible_tmp %}
{{ cible_tmp }}
{# Les autres cas sont ignoré #}
{% endif %}
- alias: Notification si l'appareil mobile est connu sinon log erreur
if:
- alias: L'appareil mobile est trouvé
condition: template
value_template: "{{ device_name != '' }}"
then:
- alias: Envoie une notification ciblé
service: notify.{{device_name}}
data:
title: "{{titre}}"
message: "{{message}}"
data:
subject: "{{resume}}"
tag: "{{tag}}"
notification_icon: "{{icone}}"
color: "{{couleur}}"
icon_url: "{{illustration}}"
sticky: "{{collant}}"
persistent: "{{persistant}}"
clickAction: "{{click}}"
actions: "{{actions}}"
else:
- alias: Log erreur
service: persistent_notification.create
metadata: {}
data:
title: Notifs avec proximité
message: >-
Vous avez choisi une entité qui ne peut pas être
notifiée<br>Veuillez enlever de la
liste<br>**{{cible_tmp}}**
- alias: Entrée n°2 de CIBLE
if:
- alias: Si entrée n°2 de CIBLE existe
condition: template
value_template: "{{ cible[1] is defined }}"
then:
- alias: Variables Personne/Appareil et Zone
variables:
cible_tmp: "{{cible[1]}}"
prox_zone: "{% if prox_zone is defined %}{{prox_zone.entity_id}}{% endif %}"
- alias: Attendre si hors zone
if:
- alias: La CIBLE est à plus de xx mètres de la zone
condition: template
value_template: >-
{% if prox_zone is defined and prox_distance is defined %} {{
(distance( prox_zone, cible_tmp ) * 1000) | int >
prox_distance | float(0) }} {% endif %}
then:
- alias: Attendre arrivée dans la zone de surveillance
wait_for_trigger:
- alias: Lorsque CIBLE est à moins de xx mètres de la zone
platform: template
value_template: >-
{{ (distance( prox_zone, cible_tmp ) * 1000) | int <
prox_distance | float(0) }}
timeout: "{{timeout}}"
continue_on_timeout: false
- alias: Identification de l'appareil mobile à notifier
variables:
device_name: >
{% if 'person.' in cible_tmp %}
{{ expand(cible_tmp)
| map(attribute='attributes.source')
| list
| join
| replace('device_tracker.', 'mobile_app_') }}
{% elif 'device_tracker.' in cible_tmp and
(state_attr(cible_tmp, 'gps_accuracy') or 0 ) > 0 %}
mobile_app_{{ state_attr(cible_tmp,'friendly_name')
| slugify }}
{% elif 'mobile_app_' in cible_tmp %}
{{ cible_tmp }}
{% endif %}
- alias: Notification si l'appareil mobile est connu sinon log erreur
if:
- alias: L'appareil mobile est trouvé
condition: template
value_template: "{{ device_name != '' }}"
then:
- alias: Envoie une notification ciblé
service: notify.{{device_name}}
data:
title: "{{titre}}"
message: "{{message}}"
data:
subject: "{{resume}}"
tag: "{{tag}}"
notification_icon: "{{icone}}"
color: "{{couleur}}"
icon_url: "{{illustration}}"
sticky: "{{collant}}"
persistent: "{{persistant}}"
clickAction: "{{click}}"
actions: "{{actions}}"
else:
- alias: Log erreur
service: persistent_notification.create
metadata: {}
data:
title: Notifs avec proximité
message: >-
Vous avez choisi une entité qui ne peut pas être
notifiée<br>Veuillez enlever de la
liste<br>**{{cible_tmp}}**
- alias: Entrée n°3 de CIBLE
if:
- alias: Si entrée n°3 de CIBLE existe
condition: template
value_template: "{{ cible[2] is defined }}"
then:
- alias: Variables Personne/Appareil et Zone
variables:
cible_tmp: "{{cible[2]}}"
prox_zone: "{% if prox_zone is defined %}{{prox_zone.entity_id}}{% endif %}"
- alias: Attendre si hors zone
if:
- alias: La CIBLE est à plus de xx mètres de la zone
condition: template
value_template: >-
{% if prox_zone is defined and prox_distance is defined %} {{
(distance( prox_zone, cible_tmp ) * 1000) | int >
prox_distance | float(0) }} {% endif %}
then:
- alias: Attendre arrivée dans la zone de surveillance
wait_for_trigger:
- alias: Lorsque CIBLE est à moins de xx mètres de la zone
platform: template
value_template: >-
{{ (distance( prox_zone, cible_tmp ) * 1000) | int <
prox_distance | float(0) }}
timeout: "{{timeout}}"
continue_on_timeout: false
- alias: Identification de l'appareil mobile à notifier
variables:
device_name: >
{% if 'person.' in cible_tmp %}
{{ expand(cible_tmp)
| map(attribute='attributes.source')
| list
| join
| replace('device_tracker.', 'mobile_app_') }}
{% elif 'device_tracker.' in cible_tmp and
(state_attr(cible_tmp, 'gps_accuracy') or 0 ) > 0 %}
mobile_app_{{ state_attr(cible_tmp,'friendly_name')
| slugify }}
{% elif 'mobile_app_' in cible_tmp %}
{{ cible_tmp }}
{% endif %}
- alias: Notification si l'appareil mobile est connu sinon log erreur
if:
- alias: L'appareil mobile est trouvé
condition: template
value_template: "{{ device_name != '' }}"
then:
- alias: Envoie une notification ciblé
service: notify.{{device_name}}
data:
title: "{{titre}}"
message: "{{message}}"
data:
subject: "{{resume}}"
tag: "{{tag}}"
notification_icon: "{{icone}}"
color: "{{couleur}}"
icon_url: "{{illustration}}"
sticky: "{{collant}}"
persistent: "{{persistant}}"
clickAction: "{{click}}"
actions: "{{actions}}"
else:
- alias: Log erreur
service: persistent_notification.create
metadata: {}
data:
title: Notifs avec proximité
message: >-
Vous avez choisi une entité qui ne peut pas être
notifiée<br>Veuillez enlever de la
liste<br>**{{cible_tmp}}**
- alias: Entrée n°4 de CIBLE
if:
- alias: Si entrée n°4 de CIBLE existe
condition: template
value_template: "{{ cible[3] is defined }}"
then:
- alias: Variables Personne/Appareil et Zone
variables:
cible_tmp: "{{cible[3]}}"
prox_zone: "{% if prox_zone is defined %}{{prox_zone.entity_id}}{% endif %}"
- alias: Attendre si hors zone
if:
- alias: La CIBLE est à plus de xx mètres de la zone
condition: template
value_template: >-
{% if prox_zone is defined and prox_distance is defined %} {{
(distance( prox_zone, cible_tmp ) * 1000) | int >
prox_distance | float(0) }} {% endif %}
then:
- alias: Attendre arrivée dans la zone de surveillance
wait_for_trigger:
- alias: Lorsque CIBLE est à moins de xx mètres de la zone
platform: template
value_template: >-
{{ (distance( prox_zone, cible_tmp ) * 1000) | int <
prox_distance | float(0) }}
timeout: "{{timeout}}"
continue_on_timeout: false
- alias: Identification de l'appareil mobile à notifier
variables:
device_name: >
{% if 'person.' in cible_tmp %}
{{ expand(cible_tmp)
| map(attribute='attributes.source')
| list
| join
| replace('device_tracker.', 'mobile_app_') }}
{% elif 'device_tracker.' in cible_tmp and
(state_attr(cible_tmp, 'gps_accuracy') or 0 ) > 0 %}
mobile_app_{{ state_attr(cible_tmp,'friendly_name')
| slugify }}
{% elif 'mobile_app_' in cible_tmp %}
{{ cible_tmp }}
{% endif %}
- alias: Notification si l'appareil mobile est connu sinon log erreur
if:
- alias: L'appareil mobile est trouvé
condition: template
value_template: "{{ device_name != '' }}"
then:
- alias: Envoie une notification ciblé
service: notify.{{device_name}}
data:
title: "{{titre}}"
message: "{{message}}"
data:
subject: "{{resume}}"
tag: "{{tag}}"
notification_icon: "{{icone}}"
color: "{{couleur}}"
icon_url: "{{illustration}}"
sticky: "{{collant}}"
persistent: "{{persistant}}"
clickAction: "{{click}}"
actions: "{{actions}}"
else:
- alias: Log erreur
service: persistent_notification.create
metadata: {}
data:
title: Notifs avec proximité
message: >-
Vous avez choisi une entité qui ne peut pas être
notifiée<br>Veuillez enlever de la
liste<br>**{{cible_tmp}}**
- alias: Entrée n°5 de CIBLE
if:
- alias: Si entrée n°5 de CIBLE existe
condition: template
value_template: "{{ cible[4] is defined }}"
then:
- alias: Variables Personne/Appareil et Zone
variables:
cible_tmp: "{{cible[4]}}"
prox_zone: "{% if prox_zone is defined %}{{prox_zone.entity_id}}{% endif %}"
- alias: Attendre si hors zone
if:
- alias: La CIBLE est à plus de xx mètres de la zone
condition: template
value_template: >-
{% if prox_zone is defined and prox_distance is defined %} {{
(distance( prox_zone, cible_tmp ) * 1000) | int >
prox_distance | float(0) }} {% endif %}
then:
- alias: Attendre arrivée dans la zone de surveillance
wait_for_trigger:
- alias: Lorsque CIBLE est à moins de xx mètres de la zone
platform: template
value_template: >-
{{ (distance( prox_zone, cible_tmp ) * 1000) | int <
prox_distance | float(0) }}
timeout: "{{timeout}}"
continue_on_timeout: false
- alias: Identification de l'appareil mobile à notifier
variables:
device_name: >
{% if 'person.' in cible_tmp %}
{{ expand(cible_tmp)
| map(attribute='attributes.source')
| list
| join
| replace('device_tracker.', 'mobile_app_') }}
{% elif 'device_tracker.' in cible_tmp and
(state_attr(cible_tmp, 'gps_accuracy') or 0 ) > 0 %}
mobile_app_{{ state_attr(cible_tmp,'friendly_name')
| slugify }}
{% elif 'mobile_app_' in cible_tmp %}
{{ cible_tmp }}
{% endif %}
- alias: Notification si l'appareil mobile est connu sinon log erreur
if:
- alias: L'appareil mobile est trouvé
condition: template
value_template: "{{ device_name != '' }}"
then:
- alias: Envoie une notification ciblé
service: notify.{{device_name}}
data:
title: "{{titre}}"
message: "{{message}}"
data:
subject: "{{resume}}"
tag: "{{tag}}"
notification_icon: "{{icone}}"
color: "{{couleur}}"
icon_url: "{{illustration}}"
sticky: "{{collant}}"
persistent: "{{persistant}}"
clickAction: "{{click}}"
actions: "{{actions}}"
else:
- alias: Log erreur
service: persistent_notification.create
metadata: {}
data:
title: Notifs avec proximité
message: >-
Vous avez choisi une entité qui ne peut pas être
notifiée<br>Veuillez enlever de la
liste<br>**{{cible_tmp}}**
- alias: Entrée n°6 de CIBLE
if:
- alias: Si entrée n°6 de CIBLE existe
condition: template
value_template: "{{ cible[5] is defined }}"
then:
- alias: Variables Personne/Appareil et Zone
variables:
cible_tmp: "{{cible[5]}}"
prox_zone: "{% if prox_zone is defined %}{{prox_zone.entity_id}}{% endif %}"
- alias: Attendre si hors zone
if:
- alias: La CIBLE est à plus de xx mètres de la zone
condition: template
value_template: >-
{% if prox_zone is defined and prox_distance is defined %} {{
(distance( prox_zone, cible_tmp ) * 1000) | int >
prox_distance | float(0) }} {% endif %}
then:
- alias: Attendre arrivée dans la zone de surveillance
wait_for_trigger:
- alias: Lorsque CIBLE est à moins de xx mètres de la zone
platform: template
value_template: >-
{{ (distance( prox_zone, cible_tmp ) * 1000) | int <
prox_distance | float(0) }}
timeout: "{{timeout}}"
continue_on_timeout: false
- alias: Identification de l'appareil mobile à notifier
variables:
device_name: >
{% if 'person.' in cible_tmp %}
{{ expand(cible_tmp)
| map(attribute='attributes.source')
| list
| join
| replace('device_tracker.', 'mobile_app_') }}
{% elif 'device_tracker.' in cible_tmp and
(state_attr(cible_tmp, 'gps_accuracy') or 0 ) > 0 %}
mobile_app_{{ state_attr(cible_tmp,'friendly_name')
| slugify }}
{% elif 'mobile_app_' in cible_tmp %}
{{ cible_tmp }}
{% endif %}
- alias: Notification si l'appareil mobile est connu sinon log erreur
if:
- alias: L'appareil mobile est trouvé
condition: template
value_template: "{{ device_name != '' }}"
then:
- alias: Envoie une notification ciblé
service: notify.{{device_name}}
data:
title: "{{titre}}"
message: "{{message}}"
data:
subject: "{{resume}}"
tag: "{{tag}}"
notification_icon: "{{icone}}"
color: "{{couleur}}"
icon_url: "{{illustration}}"
sticky: "{{collant}}"
persistent: "{{persistant}}"
clickAction: "{{click}}"
actions: "{{actions}}"
else:
- alias: Log erreur
service: persistent_notification.create
metadata: {}
data:
title: Notifs avec proximité
message: >-
Vous avez choisi une entité qui ne peut pas être
notifiée<br>Veuillez enlever de la
liste<br>**{{cible_tmp}}**
fields:
description:
name: Description
description: >-
Retient les notifications jusqu'à retour en zone de la personne ou de
l'appareil mobile. Les notifications peuvent être habillée et trois
boutons action peuvent être ajouté
selector:
text:
multiline: true
personnes:
name: Personnes
description: >-
Qui notifier ? Personnes ou appareil mobile. Vide pour notifier tous les
appareils
selector:
target: {}
default:
entity_id:
- person.USER1
- person.USER2
prox_zone:
name: Zone
description: Zone à surveiller. Si vide, pas de détection retour zone
selector:
target: {}
default:
entity_id: zone.home
prox_distance:
name: Distance
description: >-
Distance pour l'entrée en zone de surveillance. Si vide, pas de détection
retour zone
selector:
number:
min: 0
max: 1000
step: 50
default: 200
timeout:
name: Timeout
description: Durée de retention des notifications. 18h si vide ou omis
selector:
time: {}
default: "18:00:00"
titre:
name: Titre
selector:
template: {}
default: Titre facultatif
message:
name: Message
description: Possible d'utiliser du HTML
selector:
template: {}
required: true
default: Placez votre message
resume:
name: Résumé
description: Texte court affiché quand la notification n'est pas déroulée
selector:
text: null
tag:
name: Tag
description: Pour remplacer une notification ayant le même tag
selector:
text: null
icone:
name: Icône
selector:
icon: {}
default: mdi:bell
couleur:
name: Couleur
description: Couleur de la notification
selector:
text: {}
illustration:
name: Illustration
description: Illustration placée à droite de la notification
selector:
text: null
collant:
name: Collé
description: Rester affiché après ouverture de la notification
selector:
boolean: {}
persistant:
name: Persistant
description: >-
Force l'affichage, doit être effacé par une autre notification. Ne
fonctionne plus depuis Android 14 et doit être renvoyé par un automate
selector:
boolean: {}
click:
name: Click
description: Action suite au clic
selector:
text: null
log:
name: log
description: Ajouter une notification persistante dans HA
selector:
boolean: {}
default: true
actions:
name: Actions
description: "Mode avancé : 3 actions de notification maximum"
selector:
object: {}
default:
- action: traitement
title: Effectué
- action: refus
title: Plus tard
- action: URI
title: Historique
uri: settings://notification_history
source:
selector:
template: {}
name: Source
description: Informations sur le déclencheur. Code à adapter...
default: >-
{% if source is defined %}{{ source }}
{% elif trigger is defined and trigger.from_state is defined %}{{
trigger.from_state.attributes.friendly_name }} depuis {{
this.attributes.friendly_name }}
{% elif trigger is defined and trigger.event and trigger.event.data.action is defined
%}réponse {{ trigger.event.data.action }} dans {{
this.attributes.friendly_name }}
{% elif this is defined %}{{ this.attributes.friendly_name }}
{% else %}déclenchement manuel depuis [nom du script où vous êtes]
{% endif %}
mode: parallel
icon: mdi:bell-badge
max: 100
Amusez vous bien avec, je reste disponible pour debugger ce qui m’a échappé et pour apporter du support sur son utilisation