HA-Gestion Piscine-1_Filtration avec AppDaemon

Intro

Dans un article précédent, je détaillais la méthode utilisée permettant de calculer le temps de filtration de la piscine, basée sur le module HACS « Pool Pump Manager », qui fonctionne très bien mais bien trop figé à mon gout. En effet nous ne pouvons pas, par exemple, modifier la répartition du temps de filtration autour de l’heure pivot figée sur 1/3 avant et 2/3 après, paramétrer l’heure pivot, changer de méthode de calcul du temps basée uniquement sur l’abaque Abacus, choisir un mode hiver.

Je vous invite quand même à le parcourir, tout n’est pas à jeter.

Pour toutes ces raisons, et ayant récemment découvert l’addon « AppDaemon », voir mon article sur ce sujet , je me suis lancé dans un développement personnel de la gestion du temps de filtration en Python.

Je n’ai pas la prétention de me déclarer « expert » en programmation et en python. C’est en fouillant sur le net que j’ai pu arriver à mes fins et aboutir à un fonctionnement satisfaisant. Il est évident que ce programme sans prétention est largement perfectible. Tous les axes d’amélioration ou d’optimisation du code seront les bienvenus en commentaire.

Mises à Jour:

Du 14/11/2022:

  • Affichage des heures « début », « fin » au format hh:mm

Du 27/08/2022:

  • Refonte et simplification du mode de calcul de la temporisation de circulation eau.
  • La modification de la temporisation est prise en compte sans besoin de réinitialiser.

Du 30/05/2022:

  • Ajout de la durée de filtration en heure (décimale). Il faut créer une entité  » input_number.duree_filtration_ete » puis modifier les fichiers « filtration_piscine.yaml (ligne 29) » et « filtration_piscine.py (ligne 218) »

Du 24/02/2022:

  • Ajout d’une entité Arrêt_Forcé: Si = On cette entité force la pompe à l’arrêt quelque soit le mode. Dans mon cas je m’en sert pour délester la consommation.

Du 17/02/2022:

  • Ajout niveau de journalisation
  • Ajout H_Pivot dans l’affichage tranche horaire « été »
  • Ajout tempo (« tempo_eau ») de prise en compte de la température après demarrage de la pompe: Dans le cas d’une mesure de température en ligne, cette temporisation permet de brasser l’eau du bassin avant de prendre en compte sa température. Avant la fin de la tempo, la mesure prise en compte dans le calcul est celle de la veille (mémorisée dans « mem_temp ») . Ce temps peut être mis à 0 si la prise de mesure est effectuée directement dans l’eau du bassin, quoique brasser l’eau quelques minutes avant de commencer le prise en compte de la T° permet de mélange les eaux de surface et de fond.

Pré requis

Mes fichiers « .yaml » sont regroupés dans un dossier « config/packages ». J’utilise la directive « !include_dir_named packages » dans mon « configuration.yaml » très pratique pour organiser son HA.

Ci après un extrait du « configuration. yaml » dont la version à jour est disponible ici.


homeassistant:
  name: Crochon
  latitude: !secret latitude
  longitude: !secret longitude
  unit_system: metric
  packages: !include_dir_named packages
  external_url: !secret external-url
  internal_url: !secret internal-url

Cahier des charges

Mon cahier des charges est le suivant:

  • Traitement des 4 modes de fonctionnement suivant:
    • Été: Temps de calcul fonction de la température de l’eau
    • Hiver: temps de filtration basé sur une heure de départ et une durée
    • Ma F: Force la pompe en marche
    • At F: Force la pompe à l’arrêt
  • En mode Été:
    • choix du modèle de calcul du temps de filtration:
      • Mode abaque Abacus
      • Mode classique: Température divisée par 2
    • Répartition du temps de filtration autour de l’heure Pivot: 50% avant et 50% après.
    • Possibilité de choisir son heure pivot entre 11:00 et 14:00 (butées modifiables dans l’application)
    • Possibilité de minorer ou majorer par un coefficient le temps de filtration (entre 60 et 140%: c’est un choix personnel basé sur mon retour d’expérience)
    • Limiter l’heure de fin à 23:59:59.
    • Temporisation de prise en compte de la température après demarrage de la pompe
  • Forçage à l’arrêt de la pompe quelque soit le mode de fonctionnement
  • Affichage de la plage de filtration dans HA
  • Enfin, avoir la main mise totale sur le programme, maîtriser les modifications et l’améliorer à ma convenance.

L’heure pivot représente selon moi le zénith du soleil, elle sert de de référence horaire dans ma programmation.

Abaque du mode Abacus

Le temps filtration est calculé suivant la courbe bleue avec un coefficient de 100%, la rouge avec un coefficient de 60% et la verte avec un coefficient de 140%. Cette formule, utilisée par Pool Pump Manager et le pluging Jeedom a été récupérée dans https://github.com/scadinot/pool.

La courbe bleue représente la valeur 100%, la courbe verte 140% et la courbe rouge 60%.

Déclaration des entités

Les entités HA utilisées dans ce fichier doivent avoir été au préalable déclarées dans HA. Dans mon cas, elle sont regroupées dans un fichier « piscine.yaml » dont le code est disponible au téléchargement ici.

####################################################
#                                                  #
#                   PISCINE                        #
#                                                  #
####################################################

utility_meter:
# usage jour  
  energy_pisc_usage_daily:
    source: sensor.pzem_pisc_energy
    cycle: daily
    tariffs:
      - hp
      - hc
# usage semaine
  energy_pisc_usage_weekly:
    source: sensor.pzem_pisc_energy
    cycle: weekly
    tariffs:
      - hp
      - hc
# usage mois
  energy_pisc_usage_monthly:
    source: sensor.pzem_pisc_energy
    cycle: monthly
    tariffs:
      - hp
      - hc
#usage an
  energy_pisc_usage_yearly:
    source: sensor.pzem_pisc_energy
    cycle: yearly
    tariffs:
      - hp
      - hc
input_number:
  # utilisé pour simuler la température de l'eau pendant les tests
  temp_piscine:
    name: Temp Eau Simul
    min: -2
    max: 35
    unit_of_measurement: °C
    icon: mdi:thermometer

# temperature de l'eau avant arret ppe
  mem_temp_piscine:
    name: Temp Eau avant arret
    min: -5
    max: 50
    unit_of_measurement: °C
    step: 0.1
    icon: mdi:thermometer
    mode: box

# temperature de l'eau avant arret ppe
  test_mem_temp_piscine:
    name: Temp Eau avant arret Test
    min: -5
    max: 50
    unit_of_measurement: °C
    step: 0.1
    icon: mdi:thermometer
    mode: box

# tempo avant prise en compte de la température de l'eau suite à demarrage marche pompe
  tempo_circulation_eau:
    name: Tempo Circulation Eau
    min: 0
    max: 3600
    unit_of_measurement: s
    step: 0.1
    icon: mdi:timer
    mode: box

# temps utilisation cartouche chlore
  temps_cartouche_chlore:
    name: Temps cartouche Chlore
    min: 0
    max: 3600
    unit_of_measurement: h
    step: 0.1
    icon: mdi:clock
    mode: box

# Seuil 1 inferieur temperature Hors gel
  hors_gel_inf_seuil1:
    name: Temp Inf Hors-Gel Seuil 1
    min: -5
    max: 0
    unit_of_measurement: °C
    icon: mdi:thermometer
# Seuil 2 inferieur temperature Hors gel
  hors_gel_inf_seuil2:
    name: Temp Inf Hors-Gel Seuil 2
    min: -10
    max: 0
    unit_of_measurement: °C
    icon: mdi:thermometer

# Durée de filtration max en hiver
  duree_filtration_max_mode_hiver:
    name: Durée filtration en Hiver
    min: 0
    max: 15
    unit_of_measurement: h
    step: 0.1
    icon: mdi:clock
    mode: box

# Durée de filtration en été calculée par appdaemon
  duree_filtration_ete:
    name: Durée filtration en Ete
    min: 0
    max: 24
    unit_of_measurement: h
    step: 0.1
    icon: mdi:clock
    mode: box

# Durée de filtration max en hiver
  filtration_coeff_abaque:
    name: Coeff filtration Piscine Abaque
    min: 60
    max: 140
    unit_of_measurement: "%"
    step: 1.0
    icon: mdi:percent
    mode: box

input_datetime:
  heure_pivot_pisc:
    name: Heure Pivot
    has_date: false
    has_time: true

  heure_ouv_volet_pisc:
    name: Heure Ouv Volet Auto
    has_date: false
    has_time: true

  heure_ferm_volet_pisc:
    name: Heure Ferm Volet Auto
    has_date: false
    has_time: true

  heure_ma_pump_pisc_hiv:
    name: Heure Ma Pompe Pisc Hiv
    has_date: false
    has_time: true

input_boolean:
  # Force la Ppe de filtration à l'arret
  piscine_arret_force:
    name: Piscine Arret Forcé
    icon: mdi:head-snowflake

  # EV appoint Piscine en mode automatique
  ev_eau_piscine:
    name: Ev Piscine
    icon: mdi:water
  
  # Volet Piscine en mode automatique
  volet_piscine_auto:
    name: Volet Piscine (Auto=1)
    icon: mdi:garage

  #cde eclairage piscine
  eclairage_piscine:
    name: Eclairage piscine
    icon: mdi:car-light-high

  # Calcul du hors gel- Mémoire de mise en hors gel
  hors_gel_valid:
    name: Valid Hors Gel Piscine (si=1)
    icon: mdi:snowflake-alert

  # Calcul du temps de filtration selon Abaque Abacus sinon mode classique
  calcul_mode_abaque:
    name: Cacul mode Abaque (si=1)
    icon: mdi:chart-bar

# selection du mode de fonctionnement de la filtration
input_select:

  mode_fonctionnement_piscine:
    name: Mode Fonct Piscine
    options:
      - "Ete"
      - "Hiver"
      - "Ma F"
      - "At F"
    icon: mdi:pool
# sert aux tests AppDaemon
  mode_fonctionnement_piscine_test:
    name: Mode Fonct Piscine
    options:
      - "Ete"
      - "Hiver"
      - "Ma F"
      - "At F"
    icon: mdi:pool
############################  Input text    
input_text:
  # Affiche la periode de Filtration
  piscine_periode_filtration:   
    name: Periode Filtration
  test_piscine_periode_filtration:   
    name: Test Periode Filtration
  
############################  Sensor
sensor:
  # Affichage du temps de fonctionnement de la pompe ce jour
  - platform: history_stats
    name: Ma Ppe Piscine Jour
    entity_id: binary_sensor.ppe_piscine_en_marche
    state: "on"
    type: time
    start: "{{ now().replace(hour=0).replace(minute=0).replace(second=0) }}"
    end: "{{ now() }}"

  # Affichage du temps de fonctionnement de l'électrovanne appoint d'eau ce jour
  - platform: history_stats
    name: Ev Eau tps ouverture jour
    entity_id: switch.ev_eau
    state: "on"
    type: time
    start: "{{ now().replace(hour=0).replace(minute=0).replace(second=0) }}"
    end: "{{ now() }}"
    
  # Affichage du temps de fonctionnement de l'électrovanne appoint d'eau sur 7 jours
  - platform: history_stats
    name: Ev eau sur 7j
    entity_id: switch.ev_eau
    state: 'on'
    type: time
    end: "{{ now().replace(hour=0, minute=0, second=0) }}"
    duration:
      days: 7
    
template:
  - sensor:
    - name: "Heure pivot Soleil"
      unique_id: "heure_pivot_soleil"
      state: >
        {{ as_timestamp(state_attr("sun.sun", "next_noon")) | timestamp_custom('%H %M')}}
      icon: mdi:weather-sunset
#{{ as_timestamp(states.sun.sun.attributes.next_noon) | timestamp_custom(' %H %M')}}

    - name: "Energie Piscine Jour"
      unique_id: "energy_pisc_daily" 
      state: >-
        {% set p = states('sensor.energy_pisc_usage_daily_hp') | float(default=0) | round(2) %}
        {% set o = states('sensor.energy_pisc_usage_daily_hc') | float(default=0) | round(2) %}
        {{ (o + p) | round(2) }}
      unit_of_measurement: "kWh"
      device_class: "energy"
      state_class: "total"  

    - name: "energy_pisc_weekly"
      state: >-
        {% set p = states('sensor.energy_pisc_usage_weekly_hp') | float(default=0) | round(2) %}
        {% set o = states('sensor.energy_pisc_usage_weekly_hc') | float(default=0) | round(2) %}
        {{ (o + p) | round(2) }}
      unit_of_measurement: "kWh"
      device_class: "energy"
      state_class: "total"  

    - name: "energy_pisc_monthly"
      state: >-
        {% set p = states('sensor.energy_pisc_usage_monthly_hp') | float(default=0) | round(2) %}
        {% set o = states('sensor.energy_pisc_usage_monthly_hc') | float(default=0) | round(2) %}
        {{ (o + p) | round(2) }}
      unit_of_measurement: "kWh"
      device_class: "energy"
      state_class: "total"  

    - name: "energy_pisc_yearly"
      state: >-
        {% set p = states('sensor.energy_pisc_usage_yearly_hp') | float(default=0) | round(2) %}
        {% set o = states('sensor.energy_pisc_usage_yearly_hc') | float(default=0) | round(2) %}
        {{ (o + p) | round(2) }}
      unit_of_measurement: "kWh"
      device_class: "energy"
      state_class: "total"  

  - binary_sensor:
    # Si la puissance electrique est superieure à 500w, on considere que la pompe est en fonctionnement
    # ou tester le switch de la ppe de filtration si pas de mesure de puissance
    # {{ is_state('switch.ppe_filtration', 'on') }}
    - name: "ppe_piscine_en_marche"
      state: >-
        {{states.sensor.pzem_pisc_puissance.state | float(default=0) > 500}} 


# Commande du volet roulant via un Cover
cover:
  - platform: template
    covers:
      volet_piscine:
        device_class: garage
        friendly_name: "Piscine"
        value_template: >-
          {% if is_state('binary_sensor.volet_piscine_ferme', 'on') %}
            closed
          {% else %}
            open
          {% endif %}        
        open_cover:
          - service: script.volet_piscine_ouverture
        close_cover:
          - service: script.volet_piscine_fermeture   
        stop_cover:
          service: script.volet_piscine_stop
        icon_template: >-
          {% if is_state('binary_sensor.volet_piscine_ferme', 'on') %}
            mdi:garage
          {% else %} 
            mdi:garage-open
          {% endif %}

Addon Appdaemon

Cet Addon, pour ceux qui ne connaissent pas, permet d’écrire des programmes en Python très élaborés, je décrit son installation dans cet article.

Vous trouverez ci après les fichiers « .yaml » et « .py » à installer dans « votre AppDaemon. »

Code de « filtration_piscine.yaml »

# Version du 14/09/2022 -> Affichage heure début/fin au format hh:mm
import hassapi as hass
import datetime
from datetime import timedelta
import time

# Variables globales
# Saisir ici les memes modes que dans HA 
TAB_MODE = ["Ete", "Hiver", "At F", "Ma F"]
# Niveau de JOURNALISATION (log): 0=rien ou 1 =info ou 2=debug 
JOURNAL=2
# RAZ du flag fin_tempo
FIN_TEMPO = 0

# Fonction de calcul du temps de filtration selon Abaque Abacus 
def duree_abaque(Temperature_eau):
    #Advanced calculation method using an abacus.
    #D = a*T^3 + b*T^2 + c*T +d
    #T est forçé a 10°C minimum
    #Formule découverte dans: https://github.com/scadinot/pool
    #Filtration en heures
    temperature_min: float = max(float(Temperature_eau), 10)
    duree = (
            0.00335 * temperature_min ** 3
            - 0.14953 * temperature_min ** 2
            + 2.43489 * temperature_min
            - 10.72859
    )
    return duree

# Fonction de calcul du temps de filtration "Classique" 
def duree_classique(Temperature_eau):
    #Methode classique temperature / 2
    temperature_min: float = max(float(Temperature_eau), 10)
    duree = temperature_min / 2
    return duree

# Fonction de Convertion Int en heure "HH:MM:SS"
def en_heure(t):
    h = int(t)
    # On retire les heures pour ne garder que les minutes.
    t = (t - h) * 60 # 0.24 * 60 = temps_restant en minutes.
    m = int(t)
    # On retire les minutes pour ne garder que les secondes.
    t = (t - m) * 60
    s = int(t)
    return "{:02d}:{:02d}:{:02d}".format(h, m, s)

########## Programme principal ###################
class FiltrationPiscine(hass.Hass): 
    def initialize(self):
        global JOURNAL, DUREE_TEMPO, FIN_TEMPO
        message_notification= "Initialisation AppDaemon Filtration Piscine."
        self.notification(message_notification,0)
        self.log(message_notification, log="error_log")
        self.notification("Journal Notif niveau:"+str(JOURNAL),0)
        self.listen_state(self.change_temp,self.args["temperature_eau"])
        self.listen_state(self.change_mode,self.args["mode_de_fonctionnement"])
        self.listen_state(self.change_coef,self.args["coef"])
        self.listen_state(self.ecretage_h_pivot,self.args["h_pivot"])
        self.listen_state(self.change_mode_calcul,self.args["mode_calcul"])
        self.listen_state(self.raz_temporisation_mesure_temp,self.args["cde_pompe"],new_state="off")
        self.listen_state(self.fin_temporisation_mesure_temp,self.args["cde_pompe"],new_state="on", duration=float(self.get_state(self.args["tempo_eau"])))        
        self.listen_state(self.change_arret_force,self.args["arret_force"])
        self.run_every(self.touteslesxminutes, "now", 5 * 60)
        self.listen_state(self.change_tempo_circulation_eau,self.args["tempo_eau"])
        #         
        # initialisation de la temporisation avant recopie temperature
        FIN_TEMPO = 0
        # Arret de la pompe sur initalisation
        self.turn_off(self.args["cde_pompe"])

# Appelé sur changement de temperature
    def change_temp(self, entity, attribute, old, new, kwargs):
        global JOURNAL
        if new!= "unavailable":
            self.notification('Appel traitement changement Temp.',2)
            self.traitement(kwargs)

# Appelé sur changement de mode de fonctionnement
    def change_mode(self, entity, attribute, old, new, kwargs):
        global JOURNAL, FIN_TEMPO
        FIN_TEMPO = 0
        self.notification('Appel traitement changement Mode.',2)
        self.traitement(kwargs)

# Appelé sur changement de coefficient
    def change_coef(self, entity, attribute, old, new, kwargs):
        global JOURNAL
        self.notification('Appel traitement changement Coef.',2)
        self.traitement(kwargs)

# Appelé sur changement de mode de calcul
    def change_mode_calcul(self, entity, attribute, old, new, kwargs):
        global JOURNAL
        self.notification('Appel traitement changement mode de calcul.',2)
        self.traitement(kwargs)

# Appelé sur changement arret forcé
    def change_arret_force(self, entity, attribute, old, new, kwargs):
        global JOURNAL
        self.notification('Appel traitement sur arret force.',2)
        self.traitement(kwargs)

# Appelé sur changement d'état de la pompe de filtrage de ON à OFF
    def raz_temporisation_mesure_temp(self, entity, attribute, old, new, kwargs):
        global JOURNAL, FIN_TEMPO
        FIN_TEMPO = 0
        self.notification('Remise a zero temporisation circulation temp.',2)
        self.notification("Flag fin tempo= "+str(FIN_TEMPO),2)
        #self.traitement(kwargs)

# Appelé sur fin temporisation suite à demarrage de la pompe
    def fin_temporisation_mesure_temp(self, entity, attribute, old, new, kwargs):
        global JOURNAL, FIN_TEMPO 
        FIN_TEMPO = 1
        self.notification('Fin temporisation circulation eau.',2)
        self.notification("Flag fin tempo= "+str(FIN_TEMPO),2)
        #self.traitement(kwargs)

# Ecretage Heure pivot entre h_pivot_min et h_pivot_max
    def ecretage_h_pivot(self, entity, attribute, old, new, kwargs):
        h_pivot= new
        h_pivot_max="14:00:00"
        h_pivot_min="11:00:00"
        if h_pivot>h_pivot_max:
            self.set_state(self.args["h_pivot"], state = h_pivot_max)
        if h_pivot<h_pivot_min:
            self.set_state(self.args["h_pivot"], state = h_pivot_min)
        self.traitement(kwargs)

# Appelé toutes les x minutes
    def touteslesxminutes(self, kwargs):
        global JOURNAL
        self.notification('Appel traitement toutes les x mn.',2)
        self.traitement(kwargs)

# Appelé sur changement temporisation circulation eau
    def change_tempo_circulation_eau(self, entity, attribute, old, new, kwargs):
        global JOURNAL, FIN_TEMPO
        FIN_TEMPO = 0
        self.notification('Appel changement tempo circulation eau.',2)
        self.notification("Flag fin tempo= "+str(FIN_TEMPO),2)
        self.initialize()

# Routine du traitement principal
    def traitement(self, kwargs):
        global JOURNAL, FIN_TEMPO
        h_locale=time.strftime('%H:%M:%S', time.localtime())
        Mesure_temperature_eau = float(self.get_state(self.args["temperature_eau"]))
        Mem_temperature_eau = float(self.get_state(self.args["mem_temp"]))
        mode_de_fonctionnement = self.get_state(self.args["mode_de_fonctionnement"])
        arret_force = self.get_state(self.args["arret_force"])
        pompe = self.args["cde_pompe"]
        pivot= self.get_state(self.args["h_pivot"])
        coef=float(self.get_state(self.args["coef"]))/100
        mode_calcul= self.get_state(self.args["mode_calcul"])
        periode_filtration=self.args["periode_filtration"]

        # Flag FIN_TEMPO
        self.notification("Flag fin tempo= "+str(FIN_TEMPO),2)
        # Temporisation avant prise en compte de la mesure de la temperature
        self.notification("Duree tempo recirculation= "+str(float(self.get_state(self.args["tempo_eau"]))),2)
        # sinon on travaille avec la memoire de la 
        # temperature avant arret de la pompe
        # mémorise la température eau de la veille.
        if FIN_TEMPO == 1:
            Temperature_eau=Mesure_temperature_eau
            self.set_value(self.args["mem_temp"], Mesure_temperature_eau)
        else:
            Temperature_eau=Mem_temperature_eau

        #  Mode Ete
        if mode_de_fonctionnement == TAB_MODE[0]:

            if mode_calcul == "on": # Calcul selon Abaque
                temps_filtration = (duree_abaque(Temperature_eau)) * coef
                nb_h_avant = en_heure(float(temps_filtration / 2))
                nb_h_apres = en_heure(float(temps_filtration / 2))
                # nb_h_avant = en_heure(float(temps_filtration / 3)) Répartition 1/3-2/3
                # nb_h_apres = en_heure(float(temps_filtration / 3*2))
                nb_h_total = en_heure(float(temps_filtration))
                self.notification("Duree Filtration Mode Abaque: "+ str(temps_filtration)[:6]+" h",2)
            
            else: # Calcul selon méthode classique
                temps_filtration = (duree_classique(Temperature_eau))*coef
                nb_h_avant = en_heure(float(temps_filtration / 2))
                nb_h_apres = en_heure(float(temps_filtration / 2))
                nb_h_total = en_heure(float(temps_filtration))
                self.notification("Duree Filtration Mode Classique: "+ str(temps_filtration)[:6]+" h",2)

            # Calcul des heures de début et fin filtration en fontion
            # du temps de filtration avant et apres l'heure pivot
            # Adapte l'heure de début de filtration à l'heure actuelle
            # Limitation de la fin de filtration à 23:59:59
            h_maintenant = timedelta(hours=int(h_locale[:2]), minutes=int(h_locale[3:5]), seconds=int(h_locale[6:8]))
            h_pivot = timedelta(hours=int(pivot[:2]), minutes=int(pivot[3:5]))
            h_avant_t = timedelta(hours=int(nb_h_avant[:2]), minutes=int(nb_h_avant[3:5]), seconds=int(nb_h_avant[6:8]))
            h_apres_t = timedelta(hours=int(nb_h_apres[:2]), minutes=int(nb_h_apres[3:5]), seconds=int(nb_h_apres[6:8]))
            h_total_t = timedelta(hours=int(nb_h_total[:2]), minutes=int(nb_h_total[3:5]), seconds=int(nb_h_total[6:8]))
            h_max_t = timedelta(hours=23, minutes=59, seconds=59)

            h_debut = h_pivot - h_avant_t
            h_fin= h_pivot + h_apres_t

#            if h_debut<h_maintenant:
#                h_debut=h_maintenant
#                h_fin=h_maintenant+h_total_t
#                h_fin= min(h_fin,h_max_t)
            h_fin= min(h_fin,h_max_t)
            message_notification= "Nb h_avant_t: "+str(h_avant_t)+"/Nb h_apres_t: "+str(h_apres_t)+"/Nb h_total_t: "+str(h_total_t)
            self.notification(message_notification,1)
            message_notification="h_debut: "+str(h_debut)+"/h_pivot: "+str(h_pivot)+"/h_fin: "+str(h_fin) 
            self.notification(message_notification,1)
            # Affichage plage horaire
            affichage_texte =f"{str(h_debut).zfill(8)[:5]}/{str(h_pivot).zfill(8)[:5]}/{str(h_fin).zfill(8)[:5]}"
            self.set_textvalue(periode_filtration,affichage_texte)
            # Ajouté le 30 mai 2022
            self.set_value("input_number.duree_filtration_ete",round(temps_filtration,2))
            # fin ajout
            # Marche pompe si dans plage horaire sinon Arret
            if self.now_is_between(str(h_debut),str(h_fin)):
                ma_ppe=1
            else:
                ma_ppe=0

# Notifications de debug
            message_notification="Mode de fonctionnement: "+mode_de_fonctionnement
            self.notification(message_notification,2)
            message_notification=" Temp Eau= "+str(Temperature_eau)
            self.notification(message_notification,2)
            message_notification="h_pivot= "+str(pivot)
            self.notification(message_notification,2)
            message_notification="coef= "+str(coef)
            self.notification(message_notification,2)

        #  Mode hiver Heure de Début + Une durée en h 
        elif mode_de_fonctionnement == TAB_MODE[1]:
            h_debut_h= self.get_state(self.args["h_debut_hiver"])
            duree= self.get_state(self.args["duree_hiver"])
            duree_h=en_heure(float(duree))
            h_debut_t = timedelta(hours=int(h_debut_h[:2]), minutes=int(h_debut_h[3:5]), seconds=int(h_debut_h[6:8]))
            duree_t = timedelta(hours=int(duree_h[:2]), minutes=int(duree_h[3:5]))
            h_fin_f = h_debut_t + duree_t
            # Affichage plage horaire
            affichage_texte =f"{str(h_debut_h).zfill(8)[:5]}/{str(h_fin_f).zfill(8)[:5]}"
            self.set_textvalue(periode_filtration,affichage_texte)

            message_notification="h_debut_h"+str(h_debut_h)+"-Duree H:"+str(duree_h)+"-H fin:"+str(h_fin_f)
            self.notification(message_notification,2)
            # Marche pompe si dans plage horaire sinon Arret
            if self.now_is_between(str(h_debut_h),str(h_fin_f)):
                ma_ppe=1
            else:
                ma_ppe=0

        # Mode Arret Forcé
        elif mode_de_fonctionnement == TAB_MODE[2]:
            ma_ppe=0
            text_affichage = "At manuel"
            self.set_textvalue(periode_filtration,text_affichage)

        # Mode Marche Forcée
        elif mode_de_fonctionnement == TAB_MODE[3]:
            ma_ppe=1
            text_affichage = "Ma manuel"
            self.set_textvalue(periode_filtration,text_affichage)
            
        # Mode Inconnu: revoir le contenu de Input_select.mode_de_fonctionnement
        else:
            message_notification="Mode de fonctionnement Piscine Inconnu: "+mode_de_fonctionnement
            self.notification(message_notification,0)

        # Calcul sortie commande pompe filtration
        # Arret pompe sur arret forcé
        if arret_force=="on":
            self.turn_off(pompe) 
            self.notification("Att sur Delestage",1)
            text_affichage = "At delestage"
            self.set_textvalue(periode_filtration,text_affichage)
        else:
            if ma_ppe==1:
                self.turn_on(pompe)
                self.notification("Ma Pompe",1)
            else:
                self.turn_off(pompe)
                self.notification("Arret Pompe",1)

    # Fonction Notification
    # message =  Texte à afficher
    # niveau = niveau de journalisation 0,1,2
    def notification(self,message,niveau):
        global JOURNAL
        heure = str(self.time())[:8]
        if niveau <= JOURNAL:
            message_notification= format(heure)+": "+ message
            self.log(message_notification, log="piscine_log")
            #self.call_service('notify/telegram', message=message_notification)
            #self.call_service('persistent_notification/create', message=message_notification)

Code de « filtration_piscine.py »

# Version du 27/08/2022
import hassapi as hass
import datetime
from datetime import timedelta
import time

# Variables globales
# Saisir ici les memes modes que dans HA 
TAB_MODE = ["Ete", "Hiver", "At F", "Ma F"]
# Niveau de JOURNALISATION (log): 0=rien ou 1 =info ou 2=debug 
JOURNAL=2 
# RAZ du flag fin_tempo
FIN_TEMPO = 0

# Fonction de calcul du temps de filtration selon Abaque Abacus 
def duree_abaque(Temperature_eau):
    #Advanced calculation method using an abacus.
    #D = a*T^3 + b*T^2 + c*T +d
    #T est forçèe a 10°C minimum
    #Formule découverte dans: https://github.com/scadinot/pool
    #Filtration en heures
    temperature_min: float = max(float(Temperature_eau), 10)
    duree = (
            0.00335 * temperature_min ** 3
            - 0.14953 * temperature_min ** 2
            + 2.43489 * temperature_min
            - 10.72859
    )
    return duree

# Fonction de calcul du temps de filtration "Classique" 
def duree_classique(Temperature_eau):
    #Methode classique temperature / 2
    temperature_min: float = max(float(Temperature_eau), 10)
    duree = temperature_min / 2
    duree_m = min(float(duree), 23)
    return duree

# Fonction de Convertion Int en heure "HH:MM:SS"
def en_heure(t):
    h = int(t)
    # On retire les heures pour ne garder que les minutes.
    t = (t - h) * 60 # 0.24 * 60 = temps_restant en minutes.
    m = int(t)
    # On retire les minutes pour ne garder que les secondes.
    t = (t - m) * 60
    s = int(t)
    return "{:02d}:{:02d}:{:02d}".format(h, m, s)

########## Programme principal ###################
class FiltrationPiscine(hass.Hass): 
    def initialize(self):
        global JOURNAL, DUREE_TEMPO, FIN_TEMPO
        message_notification= "Initialisation AppDaemon Filtration Piscine."
        self.notification(message_notification,0)
        self.log(message_notification, log="error_log")
        self.notification("Journal Notif niveau:"+str(JOURNAL),0)
        self.listen_state(self.change_temp,self.args["temperature_eau"])
        self.listen_state(self.change_mode,self.args["mode_de_fonctionnement"])
        self.listen_state(self.change_coef,self.args["coef"])
        self.listen_state(self.ecretage_h_pivot,self.args["h_pivot"])
        self.listen_state(self.change_mode_calcul,self.args["mode_calcul"])
        self.listen_state(self.raz_temporisation_mesure_temp,self.args["cde_pompe"],new_state="off")
        self.listen_state(self.fin_temporisation_mesure_temp,self.args["cde_pompe"],new_state="on", duration=float(self.get_state(self.args["tempo_eau"])))        
        self.listen_state(self.change_arret_force,self.args["arret_force"])
        self.run_every(self.touteslesxminutes, "now", 5 * 60)
        self.listen_state(self.change_tempo_circulation_eau,self.args["tempo_eau"])
        #         
        # initialisation de la temporisation avant recopie temperature
        FIN_TEMPO = 0
        # Arret de la pompe sur initalisation
        self.turn_off(self.args["cde_pompe"])

# Appelé sur changement de temperature
    def change_temp(self, entity, attribute, old, new, kwargs):
        global JOURNAL
        if new!= "unavailable":
            self.notification('Appel traitement changement Temp.',2)
            self.traitement(kwargs)

# Appelé sur changement de mode de fonctionnement
    def change_mode(self, entity, attribute, old, new, kwargs):
        global JOURNAL, FIN_TEMPO
        FIN_TEMPO = 0
        self.notification('Appel traitement changement Mode.',2)
        self.traitement(kwargs)

# Appelé sur changement de coefficient
    def change_coef(self, entity, attribute, old, new, kwargs):
        global JOURNAL
        self.notification('Appel traitement changement Coef.',2)
        self.traitement(kwargs)

# Appelé sur changement de mode de calcul
    def change_mode_calcul(self, entity, attribute, old, new, kwargs):
        global JOURNAL
        self.notification('Appel traitement changement mode de calcul.',2)
        self.traitement(kwargs)

# Appelé sur changement arret forcé
    def change_arret_force(self, entity, attribute, old, new, kwargs):
        global JOURNAL
        self.notification('Appel traitement sur arret force.',2)
        self.traitement(kwargs)

# Appelé sur changement d'état de la pompe de filtrage de ON à OFF
    def raz_temporisation_mesure_temp(self, entity, attribute, old, new, kwargs):
        global JOURNAL, FIN_TEMPO
        FIN_TEMPO = 0
        self.notification('Remise a zero temporisation circulation temp.',2)
        self.notification("Flag fin tempo= "+str(FIN_TEMPO),2)
        #self.traitement(kwargs)

# Appelé sur fin temporisation suite à demarrage de la pompe
    def fin_temporisation_mesure_temp(self, entity, attribute, old, new, kwargs):
        global JOURNAL, FIN_TEMPO 
        FIN_TEMPO = 1
        self.notification('Fin temporisation circulation eau.',2)
        self.notification("Flag fin tempo= "+str(FIN_TEMPO),2)
        #self.traitement(kwargs)

# Ecretage Heure pivot entre h_pivot_min et h_pivot_max
    def ecretage_h_pivot(self, entity, attribute, old, new, kwargs):
        h_pivot= new
        h_pivot_max="14:00:00"
        h_pivot_min="11:00:00"
        if h_pivot>h_pivot_max:
            self.set_state(self.args["h_pivot"], state = h_pivot_max)
        if h_pivot<h_pivot_min:
            self.set_state(self.args["h_pivot"], state = h_pivot_min)
        self.traitement(kwargs)

# Appelé toutes les x minutes
    def touteslesxminutes(self, kwargs):
        global JOURNAL
        self.notification('Appel traitement toutes les x mn.',2)
        self.traitement(kwargs)

# Appelé sur changement temporisation circulation eau
    def change_tempo_circulation_eau(self, entity, attribute, old, new, kwargs):
        global JOURNAL, FIN_TEMPO
        FIN_TEMPO = 0
        self.notification('Appel changement tempo circulation eau.',2)
        self.notification("Flag fin tempo= "+str(FIN_TEMPO),2)
        self.initialize()

# Routine du traitement principal
    def traitement(self, kwargs):
        global JOURNAL, FIN_TEMPO
        h_locale=time.strftime('%H:%M:%S', time.localtime())
        Mesure_temperature_eau = float(self.get_state(self.args["temperature_eau"]))
        Mem_temperature_eau = float(self.get_state(self.args["mem_temp"]))
        mode_de_fonctionnement = self.get_state(self.args["mode_de_fonctionnement"])
        arret_force = self.get_state(self.args["arret_force"])
        pompe = self.args["cde_pompe"]
        pivot= self.get_state(self.args["h_pivot"])
        coef=float(self.get_state(self.args["coef"]))/100
        mode_calcul= self.get_state(self.args["mode_calcul"])
        periode_filtration=self.args["periode_filtration"]

        # Flag FIN_TEMPO
        self.notification("Flag fin tempo= "+str(FIN_TEMPO),2)
        # Temporisation avant prise en compte de la mesure de la temperature
        self.notification("Duree tempo recirculation= "+str(float(self.get_state(self.args["tempo_eau"]))),2)
        # sinon on travaille avec la memoire de la 
        # temperature avant arret de la pompe
        # mémorise la température eau de la veille.
        if FIN_TEMPO == 1:
            Temperature_eau=Mesure_temperature_eau
            self.set_value(self.args["mem_temp"], Mesure_temperature_eau)
        else:
            Temperature_eau=Mem_temperature_eau

        #  Mode Ete
        if mode_de_fonctionnement == TAB_MODE[0]:

            if mode_calcul == "on": # Calcul selon Abaque
                temps_filtration = (duree_abaque(Temperature_eau)) * coef
                nb_h_avant = en_heure(float(temps_filtration / 2))
                nb_h_apres = en_heure(float(temps_filtration / 2))
                # nb_h_avant = en_heure(float(temps_filtration / 3)) Répartition 1/3-2/3
                # nb_h_apres = en_heure(float(temps_filtration / 3*2))
                nb_h_total = en_heure(float(temps_filtration))
                self.notification("Duree Filtration Mode Abaque: "+ str(temps_filtration)[:6]+" h",2)
            
            else: # Calcul selon méthode classique
                temps_filtration = (duree_classique(Temperature_eau))*coef
                nb_h_avant = en_heure(float(temps_filtration / 2))
                nb_h_apres = en_heure(float(temps_filtration / 2))
                nb_h_total = en_heure(float(temps_filtration))
                self.notification("Duree Filtration Mode Classique: "+ str(temps_filtration)[:6]+" h",2)

            # Calcul des heures de début et fin filtration en fontion
            # du temps de filtration avant et apres l'heure pivot
            # Adapte l'heure de début de filtration à l'heure actuelle
            # Limitation de la fin de filtration à 23:59:59
            h_maintenant = timedelta(hours=int(h_locale[:2]), minutes=int(h_locale[3:5]), seconds=int(h_locale[6:8]))
            h_pivot = timedelta(hours=int(pivot[:2]), minutes=int(pivot[3:5]))
            h_avant_t = timedelta(hours=int(nb_h_avant[:2]), minutes=int(nb_h_avant[3:5]), seconds=int(nb_h_avant[6:8]))
            h_apres_t = timedelta(hours=int(nb_h_apres[:2]), minutes=int(nb_h_apres[3:5]), seconds=int(nb_h_apres[6:8]))
            h_total_t = timedelta(hours=int(nb_h_total[:2]), minutes=int(nb_h_total[3:5]), seconds=int(nb_h_total[6:8]))
            h_max_t = timedelta(hours=23, minutes=59, seconds=59)

            h_debut = h_pivot - h_avant_t
            h_fin= h_pivot + h_apres_t

#            if h_debut<h_maintenant:
#                h_debut=h_maintenant
#                h_fin=h_maintenant+h_total_t
#                h_fin= min(h_fin,h_max_t)
            h_fin= min(h_fin,h_max_t)
            message_notification= "Nb h_avant_t: "+str(h_avant_t)+"/Nb h_apres_t: "+str(h_apres_t)+"/Nb h_total_t: "+str(h_total_t)
            self.notification(message_notification,1)
            message_notification="h_debut: "+str(h_debut)+"/h_pivot: "+str(h_pivot)+"/h_fin: "+str(h_fin) 
            self.notification(message_notification,1)
            # Affichage plage horaire
            affichage_texte =str(h_debut)[:5]+"/"+str(h_pivot)[:5]+"/"+str(h_fin)[:5]
            self.set_textvalue(periode_filtration,affichage_texte)
            # Ajouté le 30 mai 2022
            self.set_value("input_number.duree_filtration_ete",round(temps_filtration,2))
            # fin ajout
            # Marche pompe si dans plage horaire sinon Arret
            if self.now_is_between(str(h_debut),str(h_fin)):
                ma_ppe=1
            else:
                ma_ppe=0

# Notifications de debug
            message_notification="Mode de fonctionnement: "+mode_de_fonctionnement
            self.notification(message_notification,2)
            message_notification=" Temp Eau= "+str(Temperature_eau)
            self.notification(message_notification,2)
            message_notification="h_pivot= "+str(pivot)
            self.notification(message_notification,2)
            message_notification="coef= "+str(coef)
            self.notification(message_notification,2)

        #  Mode hiver Heure de Début + Une durée en h 
        elif mode_de_fonctionnement == TAB_MODE[1]:
            h_debut_h= self.get_state(self.args["h_debut_hiver"])
            duree= self.get_state(self.args["duree_hiver"])
            duree_h=en_heure(float(duree))
            h_debut_t = timedelta(hours=int(h_debut_h[:2]), minutes=int(h_debut_h[3:5]), seconds=int(h_debut_h[6:8]))
            duree_t = timedelta(hours=int(duree_h[:2]), minutes=int(duree_h[3:5]))
            h_fin_f = h_debut_t + duree_t
            # Affichage plage horaire
            affichage_texte =str(h_debut_h)[:5]+"/"+str(h_fin_f)[:5]
            self.set_textvalue(periode_filtration,affichage_texte)

            message_notification="h_debut_h"+str(h_debut_h)+"-Duree H:"+str(duree_h)+"-H fin:"+str(h_fin_f)
            self.notification(message_notification,2)
            # Marche pompe si dans plage horaire sinon Arret
            if self.now_is_between(str(h_debut_h),str(h_fin_f)):
                ma_ppe=1
            else:
                ma_ppe=0

        # Mode Arret Forcé
        elif mode_de_fonctionnement == TAB_MODE[2]:
            ma_ppe=0
            text_affichage = "At manuel"
            self.set_textvalue(periode_filtration,text_affichage)

        # Mode Marche Forcée
        elif mode_de_fonctionnement == TAB_MODE[3]:
            ma_ppe=1
            text_affichage = "Ma manuel"
            self.set_textvalue(periode_filtration,text_affichage)
            
        # Mode Inconnu: revoir le contenu de Input_select.mode_de_fonctionnement
        else:
            message_notification="Mode de fonctionnement Piscine Inconnu: "+mode_de_fonctionnement
            self.notification(message_notification,0)

        # Calcul sortie commande pompe filtration
        # Arret pompe sur arret forcé
        if arret_force=="on":
            self.turn_off(pompe) 
            self.notification("Att sur Delestage",1)
            text_affichage = "At delestage"
            self.set_textvalue(periode_filtration,text_affichage)
        else:
            if ma_ppe==1:
                self.turn_on(pompe)
                self.notification("Ma Pompe",1)
            else:
                self.turn_off(pompe)
                self.notification("Arret Pompe",1)

    # Fonction Notification
    # message =  Texte à afficher
    # niveau = niveau de journalisation 0,1,2
    def notification(self,message,niveau):
        global JOURNAL
        heure = str(self.time())[:8]
        if niveau <= JOURNAL:
            message_notification= format(heure)+": "+ message
            self.log(message_notification, log="piscine_log")
            #self.call_service('notify/telegram', message=message_notification)
            #self.call_service('persistent_notification/create', message=message_notification)

Interfaces

Interface simplifiée

Le minimum syndical vous permettant d’effectuer des essais.

type: entities
entities:
  - entity: input_select.mode_fonctionnement_piscine
  - entity: input_number.temp_piscine
  - entity: input_datetime.heure_pivot_pisc
  - entity: switch.test_ppe_piscine
  - entity: input_number.filtration_coeff_abaque
  - entity: input_boolean.calcul_mode_abaque
  - entity: input_datetime.heure_ma_pump_pisc_hiv
  - entity: input_number.duree_filtration_max_mode_hiver
  - entity: input_text.piscine_periode_filtration
  - entity: input_number.duree_filtration_ete
  - entity: switch.ppe_filtration
  - entity: binary_sensor.ppe_piscine_en_marche
  - entity: sensor.ma_ppe_piscine_jour
  - entity: sensor.tdeg_eau_piscine
  - entity: sensor.temp_piscine

Dwain Lovelace

Personnellement, j’utilisé l’intégration HACS « Dwain lovelace », voir mon article sur cette excellente intégration HACS.

La dernière version est disponible au téléchargement ici. Vous pouvez adapter les cartes à votre environnement.

Et Pool_pump-Manager?

Et bien si vous aviez suivi mon article https://domo.rem81.com/home-assistant-gestion-piscine-filtration-poolpump/ et migré vers ce mode de gestion du temps de filtration avc AppDaemon, vous pouvez supprimer:

  • le HACS Pool_Pump_Manager
  • Les automatimes « « calcul fonctionnement pompe » et « mémorisation température avant arret »
  • Prendre la dernière version de l’automatisme « hors_gel »

Conclusion

Cet article va surement évolué dans le temps, il reste quelques améliorations ou évolutions à apporter.

N’hésitez pas à me laisser en commentaire, des critiques négatives ou positives constructrices sur le sujet.

Liste des publications en lien avec cet article:

  1. Filtration avec « AppDaemon » ou avec « Pool Pump Manager« 
  2. Mesure de puissance électrique
  3. Mise à niveau automatique
  4. Mesure du pH
  5. Régulation du Ph
  6. Mise Hors Gel
  7. Mesure de pression

73 Comments on “HA-Gestion Piscine-1_Filtration avec AppDaemon”

  1. Bravo pour tous ces outils sur HA pour gérer la piscine : c’est EXACTEMENT ce que je cherchais …

    Je pense que je vais y aller petit-à-petit: d’abord le contrôle de la pompe, puis l’hors-gel, et ensuite, je prend un grand souffle pour attaquer le Ph 🙂

    J’ai deux petites questoins, si ça ne vous dérange pas:
    – Qu’est-ce que vous appelez « l’heure pivot » ?
    – Quid de la stabilité de HA avec ce cette solution basée AppDeamon ? Pas d’impact lors de fréquentes mises-à-jour de HA ?

    Merci de votre aide et chapeau encore une fois!

    1. Merci pour ton retour, c’est sympa de ta part. C’est autour de l’heure pivot qu’est répartie la durée de filtration, elle correspond en général au zénith du soleil. par ex pour hp=13:00 si 120 mn de durée de filtration, début à 12:00, fin à 14:00. Dans mon appli la répartition est 50/50, dans l’appli pool manager que j’utilisais avant c’était 1/3 2/3. Concernant la stabilité de APPDaemon et mes MàJ, je n’ai pas de problème, c’est stable. A te lire quand tu aura avancé sur le sujet. Bon courage

  2. Tout d’abord un grand merci et bravo rem81 pour tous ces tutos forts utiles!
    J’avoue que je patauge un peu, mes connaissances sur HA et yaml étant un peu limitées, j’aurais bien besoin d’un coup de main..
    J’ai d’abord commencé par appliquer ton tuto avec Pool Pump Manager et je m’en suis sorti. Puis j’ai vu ce que tu as fait avec appdaemon et la possibilité de pouvoir régler le coeff de filtration, de la balle! C’est exactement ce que je cherchais.
    Par contre je suis un peu perdu là.. j’ai installé Appdaemon, j’ai configuré appdaemon.yaml, j’ai copié et adapté les filtration_piscine.py et .yaml dans le dossier apps. J’ai aussi configuré le piscine.yaml déclaré dans HA.
    Du coup il faut que désinstalle pool pump manager?
    Et les 3 automatisations piscine-hors-gel + calcul fonctionnement pompe + mémorisation température avant arrêt, il faut les garder ou c’est plus utile?
    Autre chose dans error.log il bloque sur « INFO Piscine: Initialisation AppDaemon Filtration Piscine » c’est normal?
    Merci encore!

    1. Bonjour merci pour ton retour. Alors oui tu peux désinstaller « pool pump manager » et supprimer les automatismes « calcul fonctionnement pompe » et « mémorisation température avant arret », ces deux fonctions étant assurées maintenant par « filtration_piscine.py ». L’atuomatisme « hors gel » est à conserver pour l’instant) mais il faut prendre la version de l’article « https://domo.rem81.com/ha-gestion-piscine-6_mode-hors-gel/ » car il agit sur le « input_select.mode_fonctionnement_piscine » ‘si tu as pris les mêmes entités)
      Pour le log « error.log » oui c’est normal par contre vérifie bien que le niveau de journalisation est egale à 2 (ligne 11 « filtration_piscine.py »: JOURNAL=2), tu dois avoir des informations dans le « piscine_log » comme expliquer au chapitre « AppDaemon.yaml » de mon article « https://domo.rem81.com/ha-addon-appdaemon-programme-python/ ».
      Voila, normalement cela doit fonctionner. Tiens moi informé
      PS: Je vais compléter l’article avec tes remarques sur les automatismes.

      1. Ah c’est vraiment sympa de répondre aussi vite!
        J’ai suivi ce que tu m’a écris et tout est ok, et j’ai bien le niveau de journalisation à 2. Tout à l’air de fonctionner, top!
        Petite question : finalement l’heure pivot tu la choisie manuellement ou tu la règle en fonction du zénith du soleil? je me dit que si je la règle automatiquement sur le zénith et qu’en plein été, l’heure pivot se fixe à 12h, pour 10 heures de filtration, ça donnerait 7h/12h/17h. C’est pas mieux de régler manuellement l’heure pivot genre sur 14h non?

        1. Bonjour, bonne nouvelle puisque ça fonctionne. Concernant l’heure pivot tu peux l’adapter à ta convenance par rapport au zenith du soleil, l’année dernière c’etait vers les 13:30 de mémoire d’ailleurs c’est un attribut de l’intégration « sun », tu peux te créer un template du genre:

          - name: "Heure pivot Soleil"
          unique_id: "heure_pivot_soleil"
          state: >
          {{ as_timestamp(states.sun.sun.attributes.next_noon) | timestamp_custom(' %H %M')}}
          icon: mdi:weather-sunset

          Je viens de l’ajouter dans le « piscine.yaml »

          Sinon pour la répartition de la filtration autour de l’heure pivot, tu peux modifier facilement dans « filtration_piscine.py » en remplacant /2 par /3 et 2/3
          if mode_calcul == "on": # Calcul selon Abaque
          temps_filtration = (duree_abaque(Temperature_eau)) * coef
          nb_h_avant = en_heure(float(temps_filtration / 2)) <-- /3 nb_h_apres = en_heure(float(temps_filtration / 2)) <-- /3*2 nb_h_total = en_heure(float(temps_filtration)) self.notification("Duree Filtration Mode Abaque: "+ str(temps_filtration)[:6]+" h",2)

          Encore merci pour ton retour d’expérience, c’est toujours intéressant d’avoir un avis extérieur, d’ailleurs si tu vois des axes d’amélioration, n’hésites pas à m’en faire part, je verrai ce que je peux faire..

          1. Merci pour ces infos. Oui j’avais testé avec l’intégration sun c’est pas mal, mais finalement je crois que je vais garder l’heure pivot en saisie manuelle c’est aussi bien.
            Il y a quelques coquilles dans les fichiers yaml.. je t’en fais part si tu veux les corriger (et si je ne me trompe pas;-)):
            – dans filtration_piscine.yaml : le input_boolean.mem_delestage n’est pas déclaré sous ce nom dans le piscine.yaml mais sous le nom input_boolean.piscine_arret_force
            – dans piscine.yaml : le commentaire dans cet input_boolean.piscine_arret_force n’est pas bon (« # EV appoint Piscine en mode automatique »).

            Petit proposition : dans piscine.yaml : binary_sensor.ppe_piscine_en_marche : pour ceux qui comme moi ne peuvent pas tester la consommation électrique de leur pompe, leur proposer de mettre : state: « {{ states.switch.pool_pump_switch.state == ‘on’ }} »

            Merci et encore bravo pour ce travail extraordinaire et pour le partage!

          2. Merci pour ces info.
            1)Dans mon cas je bloque la filtration si ma production photovoltaïque me permet de faire fonctionner un autre équipement, d’où l’entité « input_boolean.mem_delestage », mais la remarque est judicieuse j’ai commenté le « filtration_piscine.yaml »
            2)J’ai mis à jour le commentaire de input_boolean.piscine_arret_force
            3)Proposition prise en compte
            Encore merci

  3. Salut ,

    Bravo pour ton Taff , chapeau !!

    Venant du monde de Jeedom , je suis en train de monter en charge mon HomeAssistant … gentiment ..:)
    J ‘utilise le super Plugin Jeedom Piscine qui est vraiment Top .

    Crois qu’il est possible de le proposer en automation ou blueprint à importer ?

    Merci par avance ,

    Minos

    1. Bonjour et merci pour ton retour
      Vu les fonctions traitées, un automatisme ou blueprint serait long et compliqué ou alors faire beaucoup plus simple avec des plages horaire de début et arret pompe par exemple.

  4. Salut rem81 !
    Venant tout juste de terminer la construction de ma piscine. je viens de me pencher sur l’automatisation de la filtration et suis tombé sur ton blog !
    En effet ton amélioration de pool pump manager est vraiment la bienvenue, donc merci pour ton travail et surtout ton partage.
    Je viens d’implémenter ton système et je confirme que tout fonctionne parfaitement bien pour ma part.
    J’aimerai savoir ce que tu as comme sonde de température ?
    J’ai pour ma part suivi ce tuto : https://faire-ca-soi-meme.fr/domotique/2020/05/11/detournement-xiaomi-aqara-temperature-exterieure-piscine/
    Et ça marche plutot pas mal, j’ai mis la sonde dans le skimmer et le boitier est positionné à côté sous la terrasse en bois (donc caché…) mais l’idée d’avoir une sonde directement sur la plomberie dans le local via un porte sonde comme la sonde pour le micro doseur de PH me parait beaucoup plus propre.
    Merci encore pour le travail !

    1. J’oubliais un petit détail, heure_pivot_soleil est « undefined » chez moi..
      Pour corriger, il faut remplacer :
      heure_pivot_soleil:
      friendly_name: « Heure pivot Soleil »
      #icon: mdi:weather-sunset
      value_template: >
      {{ as_timestamp(value_templates.sun.sun.attributes.next_noon) | timestamp_custom(‘ %H %M’)}}

      par ça :
      heure_pivot_soleil:
      friendly_name: « Heure pivot Soleil »
      #icon: mdi:weather-sunset
      value_template: >
      {{ as_timestamp(state_attr(« sun.sun », « next_noon »)) | timestamp_custom(‘ %H %M’) | timestamp_custom(‘ %H %M’)}}

    2. Bonjour merci pour ton retour, ça fait plaisir. Concernant la Température j’utilise une sonde pt100 relié à un système wago 750 communiquant en modbus c’est de l’industriel mais, à moindre coût peux utiliser une sonde ds18b20 qui se connecte facilement à un esp😉

  5. Hello,

    je suis en train de mettre en place cette methode et suis encore novice sur HA…

    les deux fichiers filtration_piscine.py et filtration_piscine .yaml sont à mettre dans quels dossiers ?
    config/appdaemon/ ou config/appdaemon/apps …?

    J ai placé le piscine.yaml dans config/packages

    Par contre concernant les entités à changer , il y en a combien à changer ?:

    commande pompe
    commande éclairage
    consommation pompe

    désolé de poser des questions si basiques ..

    Merci par avance ,

    Minos

    1. complément infos :

      Pour les fichiers , je les ai mis sous config/appdaemon/apps , je pense que c ‘est bon ?

      par contre j ai la période de filtration qui reste à unknown ..

      1. Bonjour , et merci de ton retour ,

        Dans le fichier piscine.yaml : il y a ça à modifier aussi ?

        source: sensor.pzem_pisc_energy => est à changer par le sensor conso de mon entité

        sous filtration_piscine.yaml :

        il y en a que 2 je pense :

        # Capteurs
        temperature_eau: sensor.sondes_temperatures_circuit_eau_piscine

        # Actionneurs
        # Switch de commande de la pompe
        cde_pompe: switch.pompe_piscine

        Merci par avance ,

        Minos

        1. Pfff , bon ben c ‘est un échec , rien ne fonctionne ..lol

          Aucune valeur ne remonte dans « période de filtration »

          aucune action lorsque je passe en mode « Ma F »

          Minos

          1. Dans appDaemon :

            J ‘ai ça:

            app.Piscine initialize_error 10/04/2022, 19:31:23
            {« totalcallbacks »: « 0 », « instancecallbacks »: « 0 »
            }

          2. te décourage pas, il faut y aller progressivement, dans le piscine.yaml, garde que les entités nécessaire au fonctionnement de l’appdaemon piscine, celle du fichier  » filtration_piscine.yaml « ,il y en une dizaine, commence par le faire fonctionner après tu ajoutera petit à petit les autres entités si tu en a besoin,la mesure de puissance, etc..

    1. Si tu as suivi ma procédure d’installation de « appdaemon », tu devrai retrouver tes fichiers log dans « /config/log », tu y a accès aussi depuis l’interface utilisateur, via le port de « 5050 » de HA (par exemple http://192.168.0.37:5050/). le port « 5050 » est celui déclaré dans la configuration de l’Addon. Les deux entités importantes à déclarer hors ‘piscine.yaml » sont le sensor mesure de température et le switch de la pompe, les huit autres peuvent sont dans « piscine.yaml »

      1. Salut ,

        Dans les journaux AppDaemon :

        piscine_log n ‘est pas trouvé a priori:

        2022-04-11 19:10:42.036519 INFO HASS: Connected to Home Assistant 2022.4.1
        2022-04-11 19:10:42.113612 INFO AppDaemon: App ‘hello_world’ added
        2022-04-11 19:10:42.116399 INFO AppDaemon: App ‘Piscine’ added
        2022-04-11 19:10:42.117358 INFO AppDaemon: Found 2 total apps
        2022-04-11 19:10:42.117831 INFO AppDaemon: Starting Apps with 2 workers and 2 pins
        2022-04-11 19:10:42.119204 INFO AppDaemon: Running on port 5050
        2022-04-11 19:10:42.253434 INFO HASS: Evaluating startup conditions
        2022-04-11 19:10:42.270764 INFO HASS: Startup condition met: hass state=RUNNING
        2022-04-11 19:10:42.271224 INFO HASS: All startup conditions met
        2022-04-11 19:10:42.315380 INFO AppDaemon: Got initial state from namespace default
        2022-04-11 19:10:42.883986 INFO AppDaemon: New client Admin Client connected
        2022-04-11 19:10:44.136105 INFO AppDaemon: Scheduler running in realtime
        2022-04-11 19:10:44.149860 INFO AppDaemon: Adding /config/appdaemon/apps to module import path
        2022-04-11 19:10:44.156082 INFO AppDaemon: Loading App Module: /config/appdaemon/apps/filtration_piscine.py
        2022-04-11 19:10:44.170857 INFO AppDaemon: Loading App Module: /config/appdaemon/apps/hello.py
        2022-04-11 19:10:44.179618 INFO AppDaemon: Initializing app hello_world using class HelloWorld from module hello
        2022-04-11 19:10:44.187049 INFO AppDaemon: Initializing app Piscine using class FiltrationPiscine from module filtration_piscine
        2022-04-11 19:10:44.366770 INFO hello_world: Hello from AppDaemon
        2022-04-11 19:10:44.368556 INFO hello_world: You are now ready to run Apps!
        2022-04-11 19:10:44.375553 ERROR Piscine: User defined log piscine_log not found
        2022-04-11 19:10:44.379370 WARNING Piscine: ————————————————————
        2022-04-11 19:10:44.379742 WARNING Piscine: Unexpected error running initialize() for Piscine
        2022-04-11 19:10:44.380303 WARNING Piscine: ————————————————————
        2022-04-11 19:10:44.385637 WARNING Piscine: Traceback (most recent call last):
        File « /usr/lib/python3.9/site-packages/appdaemon/app_management.py », line 165, in initialize_app
        await utils.run_in_executor(self, init)
        File « /usr/lib/python3.9/site-packages/appdaemon/utils.py », line 337, in run_in_executor
        response = future.result()
        File « /usr/lib/python3.9/concurrent/futures/thread.py », line 52, in run
        result = self.fn(*self.args, **self.kwargs)
        File « /config/appdaemon/apps/filtration_piscine.py », line 55, in initialize
        self.notification(message_notification,0)
        File « /config/appdaemon/apps/filtration_piscine.py », line 287, in notification
        self.log(message_notification, log= »piscine_log »)
        File « /usr/lib/python3.9/site-packages/appdaemon/adapi.py », line 155, in log
        self._log(logger, msg, *args, **kwargs)
        File « /usr/lib/python3.9/site-packages/appdaemon/adapi.py », line 96, in _log
        logger.log(self._logging.log_levels[level], msg, *args, **kwargs)
        AttributeError: ‘NoneType’ object has no attribute ‘log’
        2022-04-11 19:10:44.386030 WARNING Piscine: ————————————————————
        2022-04-11 19:10:44.389488 INFO AppDaemon: App initialization complete

        1. Bon ben j ‘ai honte ….Piou …

          j ‘ai trouvé mon erreur de NAZE !!

          voici mon chemin : /config/appdaemon/log/piscine.log

          et non pas /config/log/piscine.log

          c ‘est nettement mieux …;

          j ‘avance ….

          A suivre

          1. Salut ,

            Bon c ‘est en place , et ça tourne !!

            A voir à l’usage avec la montée des températures .

            Tu crois que c’est possible d’avoir l’info du temps de filtration en visuel ?

            et un suivi chronologique de la temperature eau air et période de filtration ?

            un peu comme le plugin piscine de Jeedom .

            En tout cas merci à toi ..

            Minos

  6. Bon , je me suis fais une carte avec une chronologie et les températures

    je partage :

    type: vertical-stack
    cards:
    – show_state: true
    show_name: true
    camera_view: auto
    type: picture-entity
    image: >-
    https://www.piscines-ibiza.com/content/uploads/2020/09/review-641-0-600×450-c-center.jpeg
    entity: sensor.ble_conductivity_kevin
    name: Piscine
    – square: false
    columns: 3
    type: grid
    cards:
    – type: custom:mini-graph-card
    name: T° Eau
    entities:
    – entity: sensor.temp_piscine
    name: Température
    color: ‘#A62F03’
    hours_to_show: 96
    – type: custom:mini-graph-card
    name: T° Air
    entities:
    – entity: sensor.netatmo_netatmo_indoor_exterieure_temperature
    name: Humidité
    color: ‘#44B0E7’
    hours_to_show: 96
    – type: custom:mini-graph-card
    name: Conso Pompe
    entities:
    – entity: sensor.pzem_pisc_energy
    name: Luminosité
    color: ‘#FFBF00’
    hours_to_show: 12
    – type: sensor
    entity: sensor.energie_piscine_jour
    graph: line
    – type: custom:mini-graph-card
    entities:
    – entity: sensor.netatmo_netatmo_indoor_exterieure_temperature
    unit: °C
    color: ‘#df6366’
    name: T° Extérieure
    – entity: sensor.temp_piscine
    unit: °C
    color: ‘#4e91d8’
    name: T° Eau
    – entity: sensor.sondes_temperatures_air_temperature_5
    unit: °C
    color: ‘#9c54b3’
    name: T° Skimmer
    – entity: switch.ppe_filtration
    color: ‘#FFFF00’
    y_axis: secondary
    smoothing: false
    aggregate_func: min
    show_state: true
    show_line: false
    show_points: true
    state_map:
    – value: ‘off’
    label: Eteinte
    – value: ‘on’
    label: En Marche
    height: 250
    hours_to_show: 48
    line_width: 2
    name: null
    points_per_hour: 2
    view_layout:
    position: main
    view_layout:
    position: sidebar

    ————–

    Minos

    1. Bonjour
      J’en déduit que tu as réussi à le faire fonctionner, content pour toi! Peux tu faire une copie d’écran de ta carte ce sera plus simple à visualiser?
      Slts

        1. C’est pas grave, je créerai la carte. J’ai vu un sensor.ble_conductivity_kevin, c’est quel type, il sert à mesurer la conductivité, donc le niveau de désinfection?

  7. Non lol, c ‘est une coquille dans la carte , c ‘est un sensor utilisé pour ma Mi Flora …lol

    Mais effectivement , ça aurait pu être la désinfection…

  8. Dis moi tu sais comment il serait possible de récupérer le temps de filtration calculé , en fait la période de filtration sur la carte ?

    Merci par avance,

    Minos

    1. Il faut insérer dans la carte l’entité que tu as déclaré à la dernière ligne dans le piscine_filtration.yaml.
      # Affichage dans HA des horaires filtration
      periode_filtration: input_text.piscine_periode_filtration

      J’ai reproduit ta carte c’est sympa et la photo donne envie!

      1. Salut ,

        Merci pour la carte !

        oui periode_filtration: input_text.piscine_periode_filtration je l’ai deja dans la carte ça remonte bien les horaires autour de l’heure pivot .

        ce que j’évoque c ‘est le nombre total d’ heures de filtration par ex:

        si la période est 11:00/12:00/12:30 soit 1h30 j aimerai pouvoir afficher ce résultat 1h30 dans la carte .

        Minos

  9. Bonjour Rem81,
    Je me suis appuyé sur ta configuration pour la gestion de ma filtration et pompe à chaleur : fonctionnement parfait au delta d’un point que je ne suis pas en capacité de régler dans l’immédiat.
    J’ai aléatoirement une micro coupure sur ma ppe de filtration, information qui me remonte via une alerte de changement d’état via une automatisation dans HA ( si pompe filtration OFF action sur pompe à chaleur OFF = Alerte sur téléphone)
    Les logs remontent un init que j’ai du mal à comprendre, si tu as un peu de temps pour me l’expliquer.
    Grand merci.
    David
    2022-05-30 15:24:34.768632 INFO Piscine: Initialisation AppDaemon Filtration Piscine.
    2022-05-30 15:24:24.834068 INFO Piscine: Initialisation AppDaemon Filtration Piscine.
    2022-05-30 15:22:51.416265 INFO Piscine: Initialisation AppDaemon Filtration Piscine.
    2022-05-30 15:22:04.957530 INFO Piscine: Initialisation AppDaemon Filtration Piscine.
    2022-05-30 15:17:17.011767 INFO Piscine: Initialisation AppDaemon Filtration Piscine.
    2022-05-30 15:17:10.875062 INFO Piscine: Initialisation AppDaemon Filtration Piscine.
    2022-05-30 14:36:58.074393 INFO Piscine: Initialisation AppDaemon Filtration Piscine.
    2022-05-30 14:33:55.094539 INFO Piscine: Initialisation AppDaemon Filtration Piscine.
    2022-05-30 14:33:45.554113 INFO Piscine: Initialisation AppDaemon Filtration Piscine.
    2022-05-30 14:33:34.921919 INFO Piscine: Initialisation AppDaemon Filtration Piscine.
    2022-05-30 14:33:24.825735 INFO Piscine: Initialisation AppDaemon Filtration Piscine.
    2022-05-30 14:29:03.960816 INFO Piscine: Initialisation AppDaemon Filtration Piscine.
    2022-05-30 14:17:16.684878 INFO Piscine: Initialisation AppDaemon Filtration Piscine.
    2022-05-30 14:17:01.941292 INFO Piscine: Initialisation AppDaemon Filtration Piscine.
    2022-05-30 14:12:48.513874 INFO Piscine: Initialisation AppDaemon Filtration Piscine.
    2022-05-30 14:10:10.056383 INFO Piscine: Initialisation AppDaemon Filtration Piscine.
    2022-05-30 14:10:04.038714 INFO Piscine: Initialisation AppDaemon Filtration Piscine.
    2022-05-30 14:09:25.550258 INFO Piscine: Initialisation AppDaemon Filtration Piscine.
    2022-05-30 14:09:20.145118 INFO Piscine: Initialisation AppDaemon Filtration Piscine.

    1. Bonjour, effectivement c’est chiant. Est ce que tes autres appli appdaemon s’initialise aussi?. est ce qu’il y a des infos dans le log error.log ou main.log?

      1. Pas d’autre appli AD, je suis tombé par hasard sur ton travail et me suis lancé 🙂
        En réponse :Les logs n’ont pas d’erreur flagrante

        Une piste possible :
        Ma température avant arrêt était de 21°C, elle se trouve à présent à 23: est ce que ce changement est normal ? Ne devrais-je pas garder la T° du dernier cycle lors de l’arrêt ?
        Dans la positive, j’ai une coquille :/
        Merci pour l’aide.

        1. Bonjour
          Lorsque la pompe se met en marche, la température de l’eau de référence est celle mémorisée avant le précédent arrêt de la pompe. Lorsque la pompe démarre la prise en compte de la T° n’est effective qu’après écoulement de la temporisation saisie dans « input_number.tempo_circulation_eau », cette fonctionnalité permet d’assurer un brassage de l’eau avant prise en compte de la T° de l’eau dans le calcul. Si tu veux, tu peux m’envoyer tes logs par mail crochonremy@gmail.com, j’essaierai de t’aider à débugger.

          1. Merci pour ton retour rapide.
            J’ai effectué en fin de journée la MAJ proposée du 30/05 :p , je suis en attente du démarrage pour apprécier ou pas le phénomène …
            Je te tiens au jus.
            Belle journée 🙂

          2. Bonjour,
            Semble solutionné ! 🙂
            Ma configuration appdaemon.yaml avait pour url l’adresse loopback (127.0.0.1) et non l’adresse de mon HA.
            Je présume que le socket sur loopback port 5050 est reseté – non persistent.

            Merci pour ton excellent travail.

  10. Au secours ! 🙂

    J’y suis très proche du Graal, du néctar suprême de la gestion de la filtration 🙂

    Est-ce que ça vous dit quelque chose l’erreur suivante:

    raise ValueError(« invalid time string: %s », time_str)
    ValueError: (‘invalid time string: %s’, ‘-1 day, 22:34:49’)

    Voici le détail:

    2022-06-10 01:14:31.037690 WARNING Piscine: ————————————————————
    2022-06-10 01:19:31.060809 WARNING Piscine: ————————————————————
    2022-06-10 01:19:31.061103 WARNING Piscine: Unexpected error in worker for App Piscine:
    2022-06-10 01:19:31.061366 WARNING Piscine: Worker Ags: {‘id’: ‘7131e0b94f174689936b888307695b6d’, ‘name’: ‘Piscine’, ‘objectid’: ‘ee599c8d9b06493a98942be4c4984b83’, ‘type’: ‘scheduler’, ‘function’: <bound method FiltrationPiscine.touteslesxminutes of >, ‘pin_app’: True, ‘pin_thread’: 0, ‘kwargs’: {‘interval’: 300, ‘__thread_id’: ‘thread-0’}}
    2022-06-10 01:19:31.061597 WARNING Piscine: ————————————————————
    2022-06-10 01:19:31.062050 WARNING Piscine: Traceback (most recent call last):
    File « /usr/lib/python3.10/site-packages/appdaemon/threading.py », line 904, in worker
    funcref(self.AD.sched.sanitize_timer_kwargs(app, args[« kwargs »]))
    File « /config/appdaemon/apps/filtration_piscine.py », line 139, in touteslesxminutes
    self.traitement(kwargs)
    File « /config/appdaemon/apps/filtration_piscine.py », line 214, in traitement
    if self.now_is_between(str(h_debut),str(h_fin)):
    File « /usr/lib/python3.10/site-packages/appdaemon/utils.py », line 226, in inner_sync_wrapper
    f = run_coroutine_threadsafe(self, coro(self, *args, **kwargs))
    File « /usr/lib/python3.10/site-packages/appdaemon/utils.py », line 346, in run_coroutine_threadsafe
    result = future.result(self.AD.internal_function_timeout)
    File « /usr/lib/python3.10/concurrent/futures/_base.py », line 446, in result
    return self.__get_result()
    File « /usr/lib/python3.10/concurrent/futures/_base.py », line 391, in __get_result
    raise self._exception
    File « /usr/lib/python3.10/site-packages/appdaemon/adapi.py », line 2175, in now_is_between
    return await self.AD.sched.now_is_between(start_time, end_time, name)
    File « /usr/lib/python3.10/site-packages/appdaemon/scheduler.py », line 699, in now_is_between
    start_time = (await self._parse_time(start_time_str, name))[« datetime »]
    File « /usr/lib/python3.10/site-packages/appdaemon/scheduler.py », line 803, in _parse_time
    raise ValueError(« invalid time string: %s », time_str)
    ValueError: (‘invalid time string: %s’, ‘-1 day, 22:34:49’)

    2022-06-10 01:19:31.062293 WARNING Piscine: ————————————————————

    Super merci !

    1. Bonjour, effectivement c’est pas top. Le calcul retourne -1 jour, quelle est est la température de l’eau et l’heure pivot?

  11. Sinon, question accessoire : est-ce que vous êtes sur ZHA ou zigbee2mqtt ?
    J’ai plein de soucis avec zha et mes composants… 🙁

    1. Bonjour, j’utilise ZHA avec un clef conbee II et une vingtaine de capteurs Sononf et aquara, j’ai pas de souci, c’est stable. La stabilité du WIFI ets primordiale, de mon coté en plus de la freebox revolution, j’ai mis deux NOVA MW12 en mode pont pour étendre mon wifi et un routeur MI4 uniquement pour mes ESP.

  12. Bonsoir,

    Merci de votre (même « vos ») réponse(s).

    Alors, l’heure pivot s’affichait à 0:00 lors que l’erreur arrivait. Mais j’ai cliqué dessus et c’est passé à 11:00h et la… miracle : plus d’erreur ! Merci beaucoup !

    Juste une question sur le WiFi : est-ce que j’ai mal compris qu’il faut avoir un bon signal wifi pour que le ZigBee fonctionne ?

    En tout cas, mon Home Assistant tombait souvent en panne en wifi sur un petit serveur médiocre (un Intel N4000). J’ai changé pour un NUC 8i3 mais wifi pas supporté … je l’ai laissé câblé. Depuis, plus de panne !

    1. Bonjour, bonne nouvelle la situation s’est débloquée, 👍car effectivement l’heure pivot doit se situer entre 11:00 et 14:00. Concernant le WiFi, je pensais au réseau WiFi général pas celui du pc ha.

  13. Rémi,

    C’est moi encore. 🙂

    L’app marche parfaitement, ce qui m’amène à autre question, cette fois pas technique : quel sont votre critère pour définir le coefficient de filtration ? Vous avez dit que c’est basé sur votre expérience … dans le doute, je l’ai laissé à 100% pour le moment.

    Merci et encore bravo !

    1. Merci pour les encouragements 😉 j’ai mis ce coeff pour éventuellement booster la filtration si ponctuellement beaucoup de baigneurs. Nous nous sommes 2 en semaine, j’ai mis 90%, le WE je boost à 100/110. Désolé de ne pas être plus précis.Bonne baignade.

  14. Bonjour Rémi,

    Après un peu moins de 14 jours de fonctionnement, tout est parfait, (j’ai ajouté en plus la pompe à chaleur via une automatisation et une gestion température.) tout ronronne !

    ***********Encore merci pour le support et ton travail*************

    Mon problème de micro coupure ne venait pas de ma loopback mais d’un capteur de luminosité lors d’un changement d’état. Pb non solutionné – seulement contourné…. :/
    Connais-tu le cpateur DIO/Chacon 54703 ? (Intégration sur HA pour la partie ON/OFF en revanche le changement d’état pour la luminosité n’est pas interprété – décodé par mon RFX. (erreur json))

    1. Bonjour, merci pour ces bonnes nouvelles 👍 les temps de filtration sont près du max en ce moment, perso je réduis le coefficient à 90%, les résultats sont corrects. Par contre j’ai pas avancé sur la régulation de chlore, basée sur une mesure d’orp, mais mes mesures sont dix fois inférieures à la normalité et pourtant ma sonde est étalonnée, et le taux de chore est bon.🤔 Et non je ne connais pas le chacon cité, j’utilise des sonof zigbee pour la détection. Pour chacon la solution est peut être de passer par l’application de configuration du rfxtx et de récupérer les codes à saisir dans L’intégration Ha.🤔

  15. Salut,
    Je viens d’installer ton plugin (hier) donc à voir dans le temps, mais c’est un super boulot. Puis en suivant les étapes il permet également de se familiariser avec python…
    Petite question, comment utilises-tu « le temps d’utilisation des cartouches chlores »?
    Penses-tu qu’il serait possible d’imaginer un compte à rebours qui tient compte de la durée de filtration?
    ex: je connais la durée d’utilisation de mon chlore. Je mets mon chlore, j’appuie sur un bouton pour signaler l’ajout, et à partir de là un compte à rebours me signale quand je devrais « théoriquement » rajouter du chlore.
    Je ne sais pas si je suis clair. Mais j’aimerais bien savoir comment tu t’en sers…
    ENntout cas un grand merci!!!!

    1. Bonjour, merci pour ton retour d’experience. Concernant la cartouche chlore, j’incrémente chaque soir un input.number avec le temps de fonctionnement du jour de la piscine que je remets à zero manuellement quand je change les stick de chlore. Ci après mon automatisme

      alias: 3_3_5 Piscine-Cartouche Chlore
      description: Mémorisation du temps d’utilisation cartouche chlore hedbo- RAZ à la main
      trigger:
      – platform: time
      at: « 23:50 »
      condition: []
      action:
      – service: input_number.set_value
      data_template:
      entity_id: input_number.temps_cartouche_chlore
      value: >-
      {% set a=states(‘input_number.temps_cartouche_chlore’)|float %} {% set
      b=states(‘sensor.ma_ppe_piscine_jour’)|float %} {{ (a + b)|round(2) }}
      mode: single

      Déclaration du sensor temps de fonctionnement jour de la pompe:
      sensor:
      # Affichage du temps de fonctionnement de la pompe ce jour
      – platform: history_stats
      name: Ma Ppe Piscine Jour
      entity_id: binary_sensor.ppe_piscine_en_marche
      state: « on »
      type: time
      start: « {{ now().replace(hour=0).replace(minute=0).replace(second=0) }} »
      end: « {{ now() }} »

      Et je m’envoie une notification si temps de fonctionnement supérieur à un seuil:
      alias: 3_3_6 Piscine-Temps utilisation cartouche chlore atteint
      description: «  »
      trigger:
      – platform: template
      value_template: |-
      {% set a=states(‘input_number.temps_cartouche_chlore’) | float %}
      {% set b=states(‘input_number.chlore_seuil_max_utilisation’) | float %}
      {{ a > b }}
      condition: []
      action:
      – service: notify.telegram
      data:
      message: >-
      Tps Utilisation cart. Chlore =
      {{states(‘input_number.temps_cartouche_chlore’)}} h {{-« \n »-}} Temps
      maxi: {{states(‘input_number.chlore_seuil_max_utilisation’)}} h
      {{-« \n »-}}{{states(« sensor.date_time ») }}
      title: Piscine Alerte Cartouche Chlore à changer!!!
      mode: single

      Voila c’est basic mais fonctionnel
      Bon courage à toi.

  16. Merci pour ta réponse rapide.
    Je vais essayer.
    Autre petite question…
    J’aimerai basculer facilement la répartition autour de l’heure pivot.
    En effet, un « mode-discret » pourrait retarder le démarrage et appliquant la règle du 1/3-2/3 afin de démarrer la pompe plus tard (je prends soin de mes voisins).
    J’ai donc créer un input-boolean dans piscine.yaml:
    mode_discret:
    name: Mode discret
    icon: mdi:bed-clock

    Je le déclare dans apps.yaml:
    # Personnalisation:
    mode_discret: input_boolean.mode_discret

    j’essaie maintenant de mettre une conditionnelle pour basculer la répartition, mais mon input n’est pas reconnu dans le fichier filtration_piscine.py:

    if mode_de_fonctionnement == TAB_MODE[0]:

    if mode_calcul == « on »: # Calcul selon Abaque
    temps_filtration = (duree_abaque(Temperature_eau)) * coef
    if mode_discret == « off »:
    nb_h_avant = en_heure(float(temps_filtration / 2))
    nb_h_apres = en_heure(float(temps_filtration / 2))
    else:
    #Répartition 1/3-2/3
    nb_h_avant = en_heure(float(temps_filtration / 3))
    nb_h_apres = en_heure(float(temps_filtration / 3*2))

    Peux-tu m’éclairer mes bases en python sont insuffisantes…
    Merci

    1. Bonjour
      il te faut ajouter une ligne du genre avant le test:
      choix_mode_discret=self.args[« mode_discret »]
      et le test du choix sera donc:
      if choix_mode_discret == « off »: etc…
      ça devrait le faire.
      Bon courage

  17. Bonjour,

    Ce programme devrait être prescrit par la médecine pour gérer les piscine.
    Je l’ai déployé avec un shelly 1PM sur un contacteur de puissance et une sonde ds18B20 dans un doigt de gant et ca marche au top.
    Seul bémol me concernant, lié à une fragilité des shelly 1PM. Parfois, ceux ci ont une tendance à se déconnecter du réseau quand ils reçoivent des ordres trop proches les uns des autres et alors… c’est le drame… une seule solution – le reboot électrique. Mais évidemment – si on est pas à la maison… catastrophe.
    Ce matin, le souci m’est arrivé car il a démarré et 14sec après – ordre d’arrêt ( j’analyse ca par rapport au fait que l’eau ait perdu 4°C entre hier soir et ce matin à cause des fortes pluies et donc grosse modif du temps de filtration).

    Est il possible de mettre une tempo min de fonctionnement genre pas de recalcul le matin avant au moins 5min de fonctionnement, ne serais ce que pour purger les tuyaux de l’eau de la veillle au soir ?

    N’ayant aucune notion en python je ne suis pas autonome… désolé

    Par avance merci

    1. Je me répons tout seul…
      Question nulle et non avenue. J’avais mal initialisé la tempe_eau => 0s
      et j’avais mal paramétré le binary sensor de « fonctionnement de la pompe »
      On verra demain si ca marche mieux
      désolé

      1. Salut Xavier. Effectivement cette tempo est utile lorsque la prise de température se situe sur la tuyauterie et que l’eau doit circuler avant de prendre en compte la T°. Perso j’ai 300 s, ce qui laisse la pompe tourner au moins 5 mn évitant ainsi des arrêts marche intempestifs.
        Bon courage.

        1. Bonjour,

          En fait, ma modification de variable d’hier n’a rien changé
          Ma variable :
          input_number.tempo_circulation_eau
          Tempo Circulation Eau
          300
          initial: null
          editable: false
          min: 0
          max: 3600
          step: 0.1
          mode: box
          unit_of_measurement: s
          icon: mdi:timer
          friendly_name: Tempo Circulation Eau

          Mes logs au démarrage ce matin ( démarrage issu du calcul 08:40
          2022-08-18 08:37:57.135699 INFO Piscine: 08:37:57: Arret Pompe
          2022-08-18 08:42:57.028413 INFO Piscine: 08:42:57: Appel traitement toutes les x mn.
          2022-08-18 08:42:57.050285 INFO Piscine: 08:42:57: Flag fin tempo= 0
          2022-08-18 08:42:57.055720 INFO Piscine: 08:42:57: Duree Filtration Mode Abaque: 10.375 h
          2022-08-18 08:42:57.061217 INFO Piscine: 08:42:57: Nb h_avant_t: 5:11:16/Nb h_apres_t: 5:11:16/Nb h_total_t: 10:22:33
          2022-08-18 08:42:57.066503 INFO Piscine: 08:42:57: h_debut: 8:40:44/h_pivot: 13:52:00/h_fin: 19:03:16
          2022-08-18 08:42:57.092313 INFO Piscine: 08:42:57: Mode de fonctionnement: Ete
          2022-08-18 08:42:57.100828 INFO Piscine: 08:42:57: Temp Eau= 26.0
          2022-08-18 08:42:57.107767 INFO Piscine: 08:42:57: h_pivot= 13:52:00
          2022-08-18 08:42:57.114445 INFO Piscine: 08:42:57: coef= 1.0
          2022-08-18 08:42:57.134048 INFO Piscine: 08:42:57: Ma Pompe
          2022-08-18 08:42:57.241854 INFO Piscine: 08:42:57: Appel traitement changement etat pompe.
          2022-08-18 08:42:57.254961 INFO Piscine: 08:42:57: Fin temporisation circulation eau.
          2022-08-18 08:43:03.936601 INFO Piscine: 08:43:03: Appel traitement changement Temp.
          2022-08-18 08:43:03.957739 INFO Piscine: 08:43:03: Flag fin tempo= 1
          2022-08-18 08:43:03.971964 INFO Piscine: 08:43:03: Duree Filtration Mode Abaque: 6.0660 h
          2022-08-18 08:43:03.978274 INFO Piscine: 08:43:03: Nb h_avant_t: 3:01:58/Nb h_apres_t: 3:01:58/Nb h_total_t: 6:03:57
          2022-08-18 08:43:04.000426 INFO Piscine: 08:43:03: h_debut: 10:50:02/h_pivot: 13:52:00/h_fin: 16:53:58
          2022-08-18 08:43:04.021406 INFO Piscine: 08:43:04: Mode de fonctionnement: Ete
          2022-08-18 08:43:04.030105 INFO Piscine: 08:43:04: Temp Eau= 21.9
          2022-08-18 08:43:04.048846 INFO Piscine: 08:43:04: h_pivot= 13:52:00
          2022-08-18 08:43:04.069977 INFO Piscine: 08:43:04: coef= 1.0
          2022-08-18 08:43:04.078769 INFO Piscine: 08:43:04: Arret Pompe

          le paramétrage de mon binary sensor de fonctionnement que j’ai changé pour le mettre en rapport avec l’état du switch qui commande la pompe comme je n’ai pas de controle de puissance consommée.
          – binary_sensor:
          # Si la puissance electrique est superieure à 500w, on considere que la pompe est en fonctionnement
          # ou tester le switch de la ppe de filtration si pas de mesure de puissance
          – name: « ppe_piscine_en_marche »
          state: >-
          {{ is_state(‘switch.ppe_filtration’, ‘on’) }}
          # {{states.sensor.pzem_pisc_puissance.state | float(default=0) > 500}}

        2. Oui, je fais pareil. Mais le problème est que lorsque l’on modifie le Tempo, ça désactive la mémorisation de la veille. Donc si la température du local est plus basse, le programme recalcule et décale la reprise de la filtration. Lorsque la pompe reprend, la température remonte et donc allonge, le soir, le temps de filtration.
          Il faudrait conserver la mémorisation, peut-être mettre une variable qui prend compte de la perte de chaleur durant la nuit. De cette manière la pompe démarre a une heure cohérente, et recalcule selon la Tempo.
          Vous en pensez quoi ?

          1. Bonjour,

            J’ai le sentiment que ce que tu décris est proche du fonctionnement nominal du programme de @rem81.
            Actuellement je n’ai pas ce fonctionnement nominal comme le montre mes log.

            Pour ma part, je viens de comprendre que la durée de tempo n’est « enregistrée » que lors de l’initialisation du programme or – je ne l’ai pas redémarré hier après la modification de mon input number durée tempo donc ma tempo devait toujours être considérée à 0…

            Affaire à suivre demain donc.

          2. Bonjour, effectivement la modification de la tempo nécessite une initialisation, ce serait à corriger ou à préciser dans le tuto. Quand tu écrit que ça désactive la mémorisation de la veille, c’est parce que avec une tempo nulle, la valeur de la température mémorisée la veille avant arrêt est immédiatement écrasé par la nouvelle température lue qui est en fait la température du local. Quand tu aura régler ta tempo à la bonne , tu n’aura plus de gros décalage de T° avec la veille et la durée de filtration sera optimisée.

  18. Bonjour,
    J’ai cette erreur sur mes logs:

    Mesure_temperature_eau = float(self.get_state(self.args[« temperature_eau »]))
    ValueError: could not convert string to float: ‘unknown’

    Serais-tu comment la corrigé a t-elle un impact sur le bon fonctionnement du programme?

    Merci

    1. Salut, la température est de l’eau est la clef du bon fonctionnement. Vérifie ton argument dans le fichier .yaml
      # Capteurs
      temperature_eau: sensor.temp_piscine –<<< ici le nom de ton sensor @+

  19. Je confirme que la réinitialisation du pluggin après avoir mis la tempo a résolu tous mes soucis

  20. Hello,

    J ‘ai une petite question concernant cette entité : Valid Hors Gel Piscine (si=1)

    Est-ce que la valeur est à passer manuellement depuis la carte ou y a t il une possibilité pour que des que la température est sous 0 C° , l’entité Valid Hors Gel Piscine (si=1) passe activée à 1 ? (en mode automatique )

    Merci par avance,

    1. En complement ,
      J ‘ai récupéré ton automation 3_1_2 Piscine-Hors-Gel pour faire de l’hivernage actif .

      Est ce la bonne pratique ?

      Merci

        1. Bjr,

          Merci pour le retour ,

          concernant cette entité : Valid Hors Gel Piscine (si=1)

          Est-ce que la valeur est à passer manuellement depuis la carte ou y a t il une possibilité pour que dès que la température est sous 0 C° , l’entité Valid Hors Gel Piscine (si=1) passe activée à 1 ? (en mode automatique )

          Merci par avance,

  21. Rémi,

    Voici une petite suggestion pour corriger un tout petit souci d’affichage:

    Lorsque Periode Filtration contient une heure inférieure à 10, elle s’affiche comme ‘8:00:’.
    L’expression suivante fait que ça s’affiche ’08:00′ :
    – Ete:
    affichage_texte = f »{str(h_debut).zfill(8)[:5]}/{str(h_pivot).zfill(8)[:5]}/{str(h_fin).zfill(8)[:5]} »

    – Hiver:
    affichage_texte = f »{str(h_debut_h).zfill(8)[:5]}/{str(h_fin_f).zfill(8)[:5]} »

    Cordialement,

    Luis

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.