Suite à une configuration un peu courte de la plage de mon serveur DHCP, j'ai eu des modules Shelly (et d'autres) qui ont un peu perdu les pédales... En IT je monitore avec des outils IP comme PRTG, mais l'idée m'est venue de monitorer les modules domotique dans Home Assistant.
Pour ça il y a l'intégration ping, une intégration qui doit dater des débuts de Home Assistant et qui ne remonte pas l'IP dans les attributs. Et on verra plus loin que ce serait bien !
La première solution
On commence donc par créer un fichier ping.yaml
dans nos packages et d'ajouter nos sensors :
binary_sensor:
- platform: ping
host: 192.168.210.63
name: "Ping : Shelly Plug S01 | Sonos SdB"
count: 3
scan_interval: 30
- platform: ping
host: 192.168.210.78
name: "Ping : Shelly Plug S02 | Sonos Terrasse"
count: 3
scan_interval: 30
- platform: ping
host: 192.168.210.104
name: "Ping : Shelly Plug S03 | Congellateurs"
count: 3
scan_interval: 30
Ensuite on crée un groupe, et comme j'en ai beaucoup j'ai cherché quelque chose pour créer un groupe en mode willcard, genre binary_sensor.ping_*
. Mais il n'existe pas grand chose et j'ai juste trouvé Groups 2.0 sous AppDaemon (mais vous n'allez pas l'installer juste pour ça !). Enfin voici le code à ajouter pour ceux qui sont habitués à la chose :
###########################################################################################
# #
# Rene Tode ( [email protected] ) #
# 2017/11/29 Germany #
# #
# wildcard groups #
# #
# arguments: #
# name: your_name #
# device_type: sensor # or any devicetype #
# entity_part: "any_part" #
# entities: # list of entities #
# - sensor.any_entity #
# hidden: False # or True #
# view: True # or False #
# assumed_state: False # or True #
# friendly_name: Your Friendly Name #
# nested_view: True # or False #
# #
###########################################################################################
import appdaemon.plugins.hass.hassapi as hass
class create_group(hass.Hass):
def initialize(self):
all_entities = self.get_state(self.args["device_type"])
entitylist = []
for entity in all_entities:
if self.args["entity_part"] in entity:
entitylist.append(entity.lower())
if "entities" in self.args:
for entity in self.args["entities"]:
entitylist.append(entity.lower())
hidden = self.args["hidden"]
view = self.args["view"]
assumed_state = self.args["assumed_state"]
friendly_name = self.args["friendly_name"]
name = "group." + self.args["name"]
if not self.args["nested_view"]:
self.set_state(name,state="on",attributes={"view": view,"hidden": hidden,"assumed_state": assumed_state,"friendly_name": friendly_name,"entity_id": entitylist})
else:
self.set_state(name + "2",state="on",attributes={"view": False,"hidden": hidden,"assumed_state": assumed_state,"friendly_name": friendly_name,"entity_id": entitylist})
self.set_state(name,state="on",attributes={"view": True,"hidden": hidden,"assumed_state": assumed_state,"friendly_name": friendly_name,"entity_id": [name + "2"]})
Et ensuite dans apps.yaml
pour créer le groupe :
pings_ip:
module: groups
class: create_group
name: ping_ip
device_type: binary_sensor
entity_part: "ping_"
entities:
- sensor.group_ping
hidden: False
view: True
assumed_state: False
friendly_name: Ping IP4
nested_view: False #creates a second group inside a viewed group
A partir de là il est possible de créer une auto_entity_card pour visualiser nos sensors :
Quant au groupe il va nous servir à envoyer des messages afin de signaler les objets inactifs. Pour ça on va créer (Merci @mathieu...) un sensor :
- platform: template
sensors:
offline_ping_sensors:
friendly_name: 'Ping Offline'
value_template: >
{% set unavailable_count = states
| selectattr('state','in', ['off', 'disconnected'])
| selectattr('entity_id','in',state_attr('group.ping_ip','entity_id'))
| map(attribute='entity_id')
| list
| length
%}
{{ unavailable_count }}
attribute_templates:
IP Fail: >
{% set unavailable_list = states
| selectattr('state','in', ['off', 'disconnected'])
| selectattr('entity_id','in',state_attr('group.ping_ip','entity_id'))
| map(attribute='entity_id')
| list
| join('\n')
| replace("binary_sensor.ping", "")
| replace("_", " ")
%}
{{ unavailable_list }}
Et une automation qui sera déclenchée par ce sensor et va nous envoyer une notification :
- alias: 'IP'
id: fc48c309-63c5-4965-bd87-fbcabc026983
initial_state: true
# mode: single
trigger:
- platform: state
entity_id: sensor.offline_ping_sensors
condition:
- condition: template
value_template: "{{ trigger.to_state.state != trigger.from_state.state }}"
action:
- delay: 00:01:00
- service: notify.slack_hass_canaletto
data_template:
title: "IP Fail"
message: "{{now().strftime('%d/%m/%Y, %H:%M')}} > IP FAIL{{ state_attr('sensor.offline_ping_sensors','IP Fail') }} Count: {{ states('sensor.offline_ping_sensors') }}"
Le résultat est basic et se borne à nous envoyer la liste des modules injoignables, c'est intéressant mais ça ne mets pas en évidence un module qui tombe à un instant t :
De plus j'aimerait bien que les notifications soient cliquables afin d'ouvrir la page web des modules Shelly. J'ai donc remis @mathieu à contribution et apres une bonne nuit de sommeil on est partit sur une autre solution complémentaire.
Le plan B
On conserve nos sensors ping, mais on les renomme afin d'y intégrer le dernier digit de l'IP dans le nom, ce qui va nous donner. On est obligé d'écrire cette IP dans le nom car on ne la retrouve pas dans les attributs du sensor :
- platform: ping
host: 192.168.210.58
name: "Ping 058 : Yeelink 01 | Desk"
count: 3
scan_interval: 30
On remarque que l'IP 58 devient 058 afin de conserver un alignement correct dans notre auto_entity_card et on va traiter ça dans l'automation ou vois apprécierait le template du message. Cette option nous impose de lister tous nos sensors dans l'automation, en attendant de trouver une façon de faire appel à un groupe ou le fichier des sensors :
- alias: 'Ping Offline'
id: '08b1556d-d816-4879-b911-bd83213dd150'
initial_state: true
mode: single
trigger:
- platform: state
entity_id:
- binary_sensor.ping_127_shelly_plug_s09_udm
- binary_sensor.ping_058_yeelight_01_desk
condition:
- condition: template
value_template: "{{ trigger.to_state.state != trigger.from_state.state }}"
action:
- service: notify.slack_hass_canaletto
data_template:
title: "IP Fail"
message: '{{now().strftime("%d/%m/%Y, %H:%M")}} > {{ trigger.to_state.state|replace("on", "IP UP")|replace("off", "IP DOWN") }} : {{ trigger.to_state.attributes.friendly_name }} : {{ trigger.entity_id | replace("_0","",1) | replace("_1","1",1) | replace("_2","2",1) | replace("binary_sensor.ping", "http://192.168.210.") | replace("_"," ",1) | truncate(24, True,"") }}'
Au passage une astuce pour récupérer une liste d'entités, vous collez ça dans Outils de développement / Templates, ça vous évitera de vous coller la liste à la main :
{% for state in states %}
{{ state.entity_id }}
{%- endfor -%}
Et le résultat est maintenant uniquement sur le module défaillant, avec son url :
Il reste quelques petites choses d'ordre cosmétique, mais ça correspond maintenant à ce que j'attendais. J'envoie ces messages dans un fil Slack qui me sert de log dynamique que je regarde quand j'ai quelque chose en panne, en opposition aux notification par SMS (urgentes) ou via l'application.
Encore merci à Mathieu qui m'a bien aidé afin de mettre en code mes idées, et à la communauté afin d'améliorer tout ça !
Utiliser un tracker NMAP
Comme me l'a rappelé un utilisateur sur HACF Il y a une autre solution qui pourrait consister à utiliser l'intégration NMAP Tracker, qui, bien que pas faite pour présente l'avantage de remonter l'adresse IP en attribut. Pour faire propre il faudra renommer les trackers ainsi crées tant au niveau de l'entity_id
que du friendly_name
qui de base reprennent ce que retourne le DNS, qui n'est pas toujours très lisible.
Ca donne ça et il n'est pas utile de bricoler l'IP pour créer un lien car celle ci est disponible en attribut, ainsi que d'autres informations potentielement utiles.
- alias: 'IP Track Offline'
id: '96017908-d46d-40cc-8d95-6b7997f5a411'
initial_state: true
mode: single
trigger:
- platform: state
entity_id:
- device_tracker.nmap_shelly_1_01
- binary_sensor.numero_3
- binary_sensor.numero_4
- binary_sensor.numero_5
condition:
- condition: template
value_template: "{{ trigger.to_state.state != trigger.from_state.state }}"
action:
- service: notify.slack_hass_canaletto
data_template:
title: "IP Fail"
message: '{{now().strftime("%d/%m/%Y, %H:%M")}} > {{ trigger.to_state.state|replace("home", "IP UP")|replace("not_home", "IP DOWN") }} : {{ trigger.to_state.attributes.friendly_name }} : http://{{ trigger.to_state.attributes.ip }}'
Pour ce résultat :
Alternatives
Il est bien sur également possible d'utiliser des outils de monitirig IP de l'IT. Au delà du monde de l'IT pro, j'en ai découvert un de très sympa, Uptime Kuma, et dont l'auteur travaille à une intégration avec Home Assistant. On y reviendra.
Pour surveiller des équipement ou sites sur internet il existe UpTime Robot qui dispose d'une intégration officielle dans HA qui remonte des binary_sensor
. C'est très pratique car il y a tout ce qu'il faut pour créer des notification sur mesure.
Conclusion
Aucune de ces solutions n'est parfaite et je continue à chercher... Mais pour trouver une solution au problème de base, je pense qu'il vaudrait mieux surveiller la disponibilité des entités. En effet j'ai remarqué que parfois un Shelly peut être disponible en IP et injoignable en CoIoT. Cela permettrait également de surveiller des équipements non IP, par exemple Zigbee...
Toutes les idées sont bienvenues et avec plaisir pour échanger, ici dans les commentaires ou sur le forum HACF..