Bonjour voila plusieurs mois que je réfléchis. j’ai proposé dans la section dev un outil mon outil
Mais je cherche aujourd’hui a ajouter une abstraction supplémentaire comme une fonction.
Aujourd’hui mon outil me permet de changer/editer un composant sans casser mes automatisations.
Mais voila! je pense a partager des automatisations un peu comme blueprint. Si mon outil peut lire un index, une automatisation peut etre dupliquée sur n’importe quel système et s’adapter.
Aujourd’hui je cherche a faire murir ce projet, le faire évoluer dans un regard.
Ainsi échanger est une façon de prendre note d’observations. Je sais que Appdaemon n’est pas accessible partout. Les IA permettent de traduire un fichier yaml en python. Claude est pour moi le plus adapté.
J’ai une vision administrateur et user de home assistant. Une famille n’a pas besoin de connaitre ce qui ce cahe sous home assistant. Et pour moi je distingue une maison connectée a une maison intelligente. Certes je suis un dev de bootcamp mais avec un vecu dans la vie, ainsi je fais évoluer mon systeme avec des questions que j’aimerai partager.
Merci de m’avoir lu
Voici le genre d’automatisation que j’ai chez moi
import appdaemon.plugins.hass.hassapi as hass
from datetime import datetime
import yaml
class ExtinctionAuto(hass.Hass):
def initialize(self):
# 🔹 Chargement de l'index centralisé
self.index_path = "/homeassistant/index.yaml"
index = self._load_index()
# 🔹 Lumières à gérer — ciblées par id dans l'index
self.lights = self._get_entities_by_ids(index, "Salon", "actionneurs", [
"Eclairage_Salon_tv",
"Eclairage_Salon_table"
])
# 🔹 Capteur de luminosité — Salon > recepteurs > Capteur luminosité
self.lux_sensor = self._get_entity_by_id(index, "Salon", "recepteurs", "Capteur luminosité")
# 🔹 Seuil lux — ajustable selon observations
self.lux_threshold = 50
# 🔹 Entités système
self.dawn_sensor = "sensor.sun_next_dawn"
# 🔹 État interne
self._done_for_cycle = False
self._rearm_handle = None
self.log(f"💡 Lumières gérées : {self.lights}")
self.log(f"🔆 Capteur lux : {self.lux_sensor} (seuil : {self.lux_threshold} lux)")
# 🔹 Écoute le capteur lux
if self.lux_sensor:
self.listen_state(self._on_lux_change, self.lux_sensor)
else:
self.log("⚠️ Capteur lux introuvable dans l'index !")
# 🔹 Programme le prochain réarmement au dawn
self._schedule_rearm()
self.log("ExtinctionAuto initialisé ✅")
# ----------------------------------------------------------
def _load_index(self):
"""Charge le fichier index.yaml."""
try:
with open(self.index_path, "r") as f:
return yaml.safe_load(f)
except Exception as e:
self.log(f"⚠️ Erreur chargement index.yaml : {e}")
return {}
def _get_entities(self, index, piece, categorie, type_entity):
"""Retourne toutes les entités d'un type dans une pièce/catégorie."""
try:
items = index["pieces"][piece][categorie]
return [i["entity"] for i in items if i.get("type") == type_entity]
except (KeyError, TypeError):
return []
def _get_entity_by_id(self, index, piece, categorie, entity_id):
"""Retourne l'entité correspondant à un id dans une pièce/catégorie."""
try:
items = index["pieces"][piece][categorie]
for i in items:
if i.get("id") == entity_id:
return i["entity"]
except (KeyError, TypeError):
pass
return None
def _get_entities_by_ids(self, index, piece, categorie, ids):
"""Retourne les entités correspondant à une liste d'ids."""
try:
items = index["pieces"][piece][categorie]
return [i["entity"] for i in items if i.get("id") in ids]
except (KeyError, TypeError):
return []
# ----------------------------------------------------------
def _schedule_rearm(self):
"""Programme le réarmement au prochain dawn."""
if self._rearm_handle:
try:
self.cancel_timer(self._rearm_handle)
except Exception:
pass
self._rearm_handle = None
next_dawn_str = self.get_state(self.dawn_sensor)
if not next_dawn_str:
self.log("⚠️ Impossible de lire sensor.sun_next_dawn")
return
try:
next_dawn_dt = datetime.fromisoformat(next_dawn_str)
self._rearm_handle = self.run_at(self._rearm_cycle, next_dawn_dt)
self.log(f"⏰ Réarmement programmé au prochain dawn : {next_dawn_dt}")
except Exception as e:
self.log(f"⚠️ Erreur parsing sensor.sun_next_dawn : {next_dawn_str} — {e}")
# ----------------------------------------------------------
def _rearm_cycle(self, kwargs):
"""
Au dawn : réarmement du cycle.
L'automatisation se réveille et surveille la montée du lux.
"""
self._done_for_cycle = False
self.log("🔄 Cycle réarmé au dawn")
# Tentative immédiate si lux déjà suffisant
self._try_extinction()
# Reprogramme pour le lendemain
self._schedule_rearm()
# ----------------------------------------------------------
def _on_lux_change(self, entity, attribute, old, new, kwargs):
"""
Le lux change → tentative d'extinction si le seuil est franchi.
Ignoré si le cycle est terminé.
"""
if self._done_for_cycle:
return
try:
lux_new = float(new)
lux_old = float(old) if old else lux_new
except (TypeError, ValueError):
return
# Uniquement à la montée en franchissant le seuil
if lux_old >= self.lux_threshold or lux_new < self.lux_threshold:
return
self.log(f"🔆 Seuil lux franchi : {lux_old:.0f} → {lux_new:.0f} lux")
self._try_extinction()
# ----------------------------------------------------------
def _try_extinction(self):
"""
Logique centrale d'extinction.
Déclencheurs :
- Franchissement du seuil lux (50 lux par défaut)
- Réarmement au dawn si lux déjà suffisant
Conditions requises pour éteindre :
1. Cycle non terminé (_done_for_cycle = False)
2. Au moins une lumière allumée
3. Lux > seuil
Fin de cycle :
- Toutes les lumières éteintes → _done_for_cycle = True
- L'automatisation dort jusqu'au prochain dawn
"""
if self._done_for_cycle:
return
lights_on = [l for l in self.lights if self.get_state(l) == "on"]
if not lights_on:
self.log("✅ Aucune lumière allumée — cycle terminé")
self._done_for_cycle = True
return
lux = self._get_lux()
if lux is None:
self.log("⚠️ Valeur lux indisponible — pas d'action")
return
if lux >= self.lux_threshold:
for light in lights_on:
self.log(f"🔴 Extinction automatique : {light} ({lux:.0f} lux)")
self.turn_off(light)
self._done_for_cycle = True
self.log("✅ Cycle terminé — en veille jusqu'au prochain dawn")
else:
self.log(f"⏳ Lux insuffisant ({lux:.0f} lux < {self.lux_threshold}) — en attente")
# ----------------------------------------------------------
def _get_lux(self):
"""Retourne la valeur lux courante, ou None si indisponible."""
try:
return float(self.get_state(self.lux_sensor))
except (TypeError, ValueError):
return None
Cette automatisation me permet d’éteindre des lumières de mon salon suivant un capteur lux. L’automatisation se met en pause et se réactive grace a sensor.sun_next_dawn. Car elle libère appdaemon d’un contrôle constant. Cette construction je l’ai eu par étapes et avec l’aide d’une IA. Naturellement nous allumons les lumières quand le soleil n’est pas levé, le capteur lux permet d’éteindre naturellement ou une autre automatisation. L’avantage de appdaemon ce n’est pas imbriquer les automatisations mais répondre a des conditions et ainsi elles sont independantes.
Ainsi dans l’evolution je veux ajouter
# 🔹 Lumières à gérer — ciblées par id dans l'index
self.lights = self._get_entities_by_ids(index, "Salon", "actionneurs", [
"Eclairage_Salon_tv",
"Eclairage_Salon_table"
])
un role qui me permet de cibler directement les lumieres en fonction de leur role sans coder en dur
Salut,
Je n’utilise pas ton apps et je n’ai surement pas tout compris mais j’ai quand même l’impression que ça (re)fait beaucoup de choses déjà plus ou moins existantes avec automatisations natives et/ou les blueprints d’automatisations. Voire des choses issues du lab HA.
- la notion de salle existe déjà nativement
- la recherche par ID/zone/type
- les nouveaux déclencheurs
- des outils de détections d’incohérence
La notion d’abstraction est intéressante, mais à titre personnel en changeant de matériel, simplement en renommant l’entité, je conserver les automatisations à jour et fonctionnelles.
Ce genre de dev « souffre » de biais:
- un besoin ultra-spécifique qui est difficile à généraliser
- une n-ième façon de traiter un besoin (en plus des automatisation, nodered etc)
- une doc qui doit tout expliquer (j’ai lu le github, tes 2 sujets, regardé la vidéo et j’ai pas vu l’ombre d’une automatisation au sens HA, au mieux j’ai vu une carte)
- un ratio temps de config/compréhension par rapport au temps passé à corriger à la main pas toujours évident
Donc pour un utilisateur lambda (ce que je ne suis sans doute au sens premier) c’est tout sauf facile à prendre en main. Il y a déjà trop de choses à apprendre pour s’éviter d’en ajouter une de plus.
Et si je compare par rapport à l’expérience vécue avec mon générateur des effets météo en CSS, personnellement j’ai pris le partie de garder l’idée mais de tout refaire en mode carte classique, l’adhésion des utilisateurs est beaucoup plus facile, mais ça reste vachement technique.
Donc sans forcément connaitre l’objectif que tu te fixes, j’aurais tendance à dire : comment faire pareil mais en plus simple (du point de vue utilisateur) et moins spécifique (du point de vue dev) c’est ça qui à mon avis dois guider tes prochaines évolutions
Je te remercie énormément d’avoir porté un intérêt. Mon outil n’est pas une automatisation mais la gestion d’un fichier d’indexation.
-
Le premier regard que j’ai eu, c’est, que ce passe t il si un composant lâche?
-
Comment une automatisation native de home assistant réagit quand on remplace ce dernier?
-
Enfin si le composant se retrouve dans plusieurs automatisations, faut il revoir tout ?
C’est pour cela que je demande conseil. Je n’ai pas toutes les réponses.
Je reconnais que ce n’est pas pour un utilisateur lambda, d’où mon idée d’en faire une chose plus ludique mais je n’ai pas encore toutes les connaissances pour.
Mon objectif était de répondre a mes 3 premières questions. Ensuite partager une idée car j’aime l’open source et enfin apprendre encore et encore. Je comprends le python mais cela reste encore un langage que j’apprends sur le tas.
Pas de souci. C’est plus clair, mais cela demande tout de même quelques précisions, car ce fichier d’indexation doit forcément impacter la structure de tes automatisations (ou alors nous n’utilisons pas les mêmes termes). Quand tu parles d’actionneurs (un terme très français !), je l’interprète immédiatement comme des triggers.
Je relève un passage de ton descriptif :
« Etant fan de appdaemon, j’ai du revoir des automatisations pour remplacer une ampoule. Je me suis posé la question comment changer un composant sans tout casser ou revoir. »
De mon point de vue, il manque le lien concret : comment ton outil fait-il la jonction entre le remplacement matériel et le code ? Qui remplace quoi, et par quel mécanisme ?
C’est une question récurrente, mais on oublie souvent d’analyser la fréquence réelle de ces pannes. Avec 10 ans de recul en domotique, je n’ai quasiment jamais été confronté au cas. Stocker du matériel de rechange (spare) est un investissement rarement rentable, surtout quand on voit la vitesse à laquelle les produits évoluent. Si toutefois cela arrive, des solutions comme Spook existent déjà. Qu’est-ce que ton développement apporte spécifiquement de plus sur ce point ?
C’est simple : elle dysfonctionne. Au mieux, elle poursuit son cycle en ignorant l’étape manquante ; au pire, elle se bloque totalement. Là encore, quel est l’avantage de ta solution ? Ajouter une couche intermédiaire entre le hardware et le système est une idée séduisante, mais elle introduit de la complexité et des risques de bugs. Le bénéfice doit être réel pour compenser cette surcharge.
A fonctions identiques, il suffit généralement de modifier le nom dans les automatisations ou, plus simple encore, de forcer l’ancien entity_id sur le nouveau matériel. Cette seconde option ne coûte quasiment rien en temps.
Moi non plus ! Si mes remarques peuvent sembler incisives, c’est uniquement pour stimuler le débat et pousser la réflexion, sans aucune animosité.
Je te soumets d’ailleurs une autre problématique : ton concept de gestion par « pièce » est intéressant, mais pourquoi définir l’organisation de la maison à l’intérieur de ton composant ? Si on doit déjà le faire dans HA pour en faire profiter les entités, le Jinja ou les recherches, cela crée une double saisie. En tant que développeur/geek passionné, je déteste répéter une tâche : si je décide demain que mon « Jardin » devient « Jardin + Terrasse », je ne veux pas avoir à mettre à jour deux systèmes différents. Et typiquement c’est le genre de truc qui arrive plus souvent qu’une panne !
Je suis d’accord avec les thermes actionneurs/recepteurs c’est une inversion de dénomination.
Home assistant travail sur entity_id par défaut et mon fichier permet aux automatisations de travailler par id (ou name) ainsi grace a ma card je peux modifier une entity_id en quelques clicks sans revoir une seule automatisation.
Pour cela j’ai api_index qui a le rôle de backend CRUD et le reste des automisations vont lire l’index comme je l’ai montre dans mon exemple d’automatisation.
Aujourd’hui mon projet serait aussi d’étendre cette idée avec un smartphone affecté a une personne, remplacer l’appareil en conservant les automatisations. J’utilise certes depuis moins longtemps home assistant mais j’ai eu le cas d’un esp32 qui m’a lâché. J’ai remplacé des objets wifi par des zigbee. Je pense que tout évolue et ce projet est scalable.