[TUTO] - Domotiser une chaudière De Dietrich type DGT130 avec le plugin MyModbus

Domotiser une chaudière De Dietrich type DGT130 à l’aide du plugin MyModbus

Sources utiles

Objectif général

Depuis un moment, je m’étais mis dans la tête de pouvoir piloter ma chaudière à condensation De Dietrich au travers de Jeedom afin notamment de mieux calibrer la température intérieure. Lorsque les consignes de JOUR et de NUIT sont réglées, la température obtenu ne correspondait pas vraiment à ce que je demandais même en tentant de compenser plus ou moins ces consignes.

Compréhension

La chaudière fonctionne à l’aide d’une sonde extérieure uniquement et ne possède pas de sonde d’ambiance intérieure. Aussi, je me suis toujours demandé comment elle pouvait parvenir à mesurer la température intérieure pour adapter sa montée en température. Après quelques recherches, j’ai compris que ce n’était pas son mode de fonctionnement mais je ne suis pas pour autant certain d’avoir bien saisit son processus n’étant pas expert du domaine.

Je pense avoir compris que la chaudière réaliserait un calcul savant basé sur la consigne demandée par l’utilisateur et la température extérieure pour faire chauffer l’eau. Elle semble conserver également un niveau de chaleur minimal pour la condensation. Toutefois, je n’ai pas compris la formule employée mais j’ai constaté que l’impact d’un changement minime de consigne ou de température extérieure semblait important sur la température de la chaudière.

Je possède également dans mon logement un variateur basique relié à ma chaudière sur la borne « S AMB A ». Objectivement, la variation ne semble pas faire grand chose et mes recherches vont me confirmer qu’il n’est pas adapté à ma chaudière.

Solutions possibles

Le plus simple pour piloter la chaudière serait de passer par une commande distante du constructeur De Dietrich. Malheureusement, elle coûte chère à l’achat (environ 200€) et à l’installation. De plus, rien ne garantie qu’elle puisse être domotisée au travers de Jeedom.

Après quelques recherches, j’ai découvert que la chaudière utilisait le protocol MODBUS mais sur un fonctionnement particulier en « bi-maître ». Cela signifie que la chaudière a la capacité de s’intégrer en cascade avec d’autres chaudières et de communiquer entre elles. Toutefois, cela entraine une capacité à échanger en maitre/esclave et donc d’écouter sur la ligne pour parler au bon moment.

Jeedom possède plusieurs plugins pour discuter en MODBUS ce qui est encourageant et m’a décider à partir sur cette voie.

L’aventure du modbus et du lien avec la chaudière

Liste des messages, adresses et paramètres

La première étape consiste a chercher les différentes informations utiles permettant de communiquer avec la chaudière. Nous sommes en présence d’un équipement particulier qui pourrait parfaitement avoir un mode de communication propriétaire avec aucune donnée à notre disposition pour communiquer. Si tel était le cas, il faudrait se lancer dans un travail fastidieux pour parvenir à trouver la trame adapter pour communiquer avec la chaudière puis ensuite trouver toutes les adresses des informations attendues.

Heureusement la magie d’internet est là ! La communauté s’est déjà penchée sur la question et un tableau complet d’adressage des chaudières De Dietrich peut être trouvé (je ne parviens pas à mettre un fichier xls en pièce jointe pour le proposer).

Le tableau identifier un numéro de registre en première colonne et donne les explications sur les informations attendues. Il faudra donc envoyer ce numéro de registre pour avoir en réponse la valeur de la donnée.

Note
Les registres présentés peuvent être utilisés pour de la lecture (MESURE) ou pour de l’écriture (PARAM). Lorsqu’il y a «  » d’indiquer dans la colonne TYPE, cela signifie généralement que le registre peut être utilisé pour récupérer une mesure (lecture) ou modifier un paramètre (écriture).

Nous avons également les réglages à utiliser pour l’échange d’informations :

Information valeur
Baudrate 9600
Data Bits 8
Parity None
Stop 1

Où récupérer les informations sur la chaudière

Là encore, Internet nous aide à comprendre que les informations pourraient être récupérées via la borne S AMB A ou via l’un des ports Mini DIN présent sur une carte électronique présente dans la facade de contrôle de la chaudière.


J’ai personnellement opté pour l’utilisation du port Mini DIN situé le plus à l’intérieur de la carte à proximité de la nappe blanche sans savoir si les deux ports sont les mêmes. Je n’ai pas non plus testé le borner S AMB A.

Comment communiquer avec la chaudière

A présent, il faut trouver le moyen de relier la chaudière à la box domotique afin qu’elle puisse envoyer et récupérer des trames MODBUS.

Il existe plusieurs possibilité pour cela :

  • via USB en reliant directement le câble à la box domotique
  • via RJ45 en reliant le câble au routeur/switch puis en récupérant les trames par le réseau
  • via WIFI en reliant le câble à un module permettant l’envoi en Wifi sur le réseau

L’important ici sera donc d’adapter son choix en fonction de la configuration de son logement. Personnellement, il n’était pas possible de me bancher directement sur la box domotique car elle et trop loin de la chaudière. Il n’était pas possible non plus de faire courir un câble réseau pour des raisons esthétiques. Aussi, la seule solution restant était d’utiliser le WIFI comme moyen de communication.

Choisir un module MODBUS

Dans mon cas, il me fallait donc un module qui puisse récupérer ou envoyer des trames RS485 et les pousser sur le réseau via le WIFI. Après quelque recherches, je me suis tourné vers le modèle URS-W610 qui permet de récupérer des trames RS232 et RS485 puis établir une liaison en RJ45 ou en WIFI.

https://amzn.eu/d/eisU92L

Câblage

Pour relier la chaudière au module, il est nécessaire d’avoir un port mini DIN. Avant d’en acheté un, il est important de s’interrogé sur l’endroit où l’on souhaite installer le module.

ATTENTION
Le module doit être à porté de WIFI si vous utiliser ce mode de communication et doit être alimenté. Aussi, bien s’assurer de ces contraintes dans votre choix !

Dans mon cas, j’avais la chance d’avoir un câble existant qui reliait la chaudière à mon variateur inutile et qui arrivait donc jusqu’à ma pièce principale. Aussi, il était simple de le réutiliser et donc de prendre un câble mini DIN d’une taille réduite.

https://amzn.eu/d/65mv87i

Une fois le câble récupéré, il faut le couper pour relier les bons PIN au module sur la borne RS485. Internet nous aide encore pour cela en expliquant qu’un port mini DIN possède 4 PIN et qu’il est nécessaire dans relier uniquement 2.

(crédit : GitHub - Benoit3/Diematic_to_MQTT: MQTT interface for De Dietrich Diematic3 heater regulator written in python)

TIPS
Pour savoir à quel fil correspond chaque PIN, il faut utiliser un multimètre pour tester la conductivité entre un PIN et chaque fil jusqu’à trouver le bon. C’est un peu fastidieux car les PIN sont rapprochés mais avec un peu de patience, on y arrive !

Configurer le module

Une fois le câblage réalisé, il faut bien entendu :

  • Brancher le mini-DIN sur le port de la carte électronique de la chaudière (j’ai utilisé celui de gauche me concernant)
  • Installer l’antenne WIFI fournie si vous utiliser le WIFI
  • Alimenter électriquement le module

TIPS
Pour s’assurer que le branchement des fils du mini-DIN a bien été fait, vous devriez voir la diode verte RTX clignoter un peu régulièrement toutes les 5 secondes environ. Si ce n’était pas le cas, vérifier que vous n’avez pas inversé les fils.

Avant toute chose, il faut se connecter au module pour accéder à sa configuration. Cela se fait en WIFI en se connectant directement au SSID du module USR-W610_XXX.

AIDE

  • Ouvrir ses paramètres réseaux et chercher le SSID USR-W610_XXX et s’y connecter
  • Ouvrir son navigateur Internet et aller à l’adresse : 10.10.100.254
  • Saisir le login/mot de passe qui sont admin pour les deux (à changer dans l’interface ensuite)

On arrive alors dans l’interface de configuration qui peut être passée en anglais à l’aide du bouton situé en haut à droite de l’interface.

Ensuite, voici les différentes pages avec la configuration utilisée chez moi (j’ai caché les informations personnelles : BSSID, SSID, mot de passe wifi, login/password).






Choisir ce qu’on veut récupérer

A partir de là, on devait pouvoir discuter avec la chaudière… mais pour dire quoi ?
En parcourant le tableau d’adressage, on peut identifier plusieurs registres intéressant que l’on peu lister ici en prévision d’une récupération via Jeedom. Par exemple :

Registre Description
503 Récupère la puissance instantanée
504 Récupère la puissance moyenne par heure
601 Récupère la température de la sonde extérieure
602 Récupère la température de la chaudière
604 Récupère la température des fumées
610 Récupère la pression du circuit de chauffage
615 Récupère la température calculée
620 Récupère la température calculée de la chaudière
650 Lecture de la température de consigne JOUR du circuit A
651 Récupère la température de consigne NUIT du circuit A
652 Récupère la température de consigne ANTIGEL du circuit A
653 Récupère le status du mode et permet également de le modifier avec JOUR = 4, NUIT = 2, ANTIGEL = 1 et AUTO = 8
14 Change la consigne JOUR du circuit A
15 Change la consigne NUIT du circuit A

Intégration dans Jeedom

Maintenant que nous savons normalement communiquer avec la chaudière et que nous savons quoi lui demander, nous allons voir comment utiliser Jeedom pour discuter avec notre chaudière et lui donner des ordres.

Plugin MyModbus

Ce plugin va permettre d’envoyer et récupérer les trames Modbus. Il s’installe comme tout plugin en allant sur le market Jeedom.

TIPS
Personnellement, je ne suis pas pervenu à faire fonctionner le plugin en mode stable. Aussi, j’ai été contraint de passer par la version beta. Cette dernière n’est pas visible pas défaut et nécessite de passer par les paramètres de Jeedom.

Une fois les dépendances installées, le démon de démarrer. Il n’est pas nécessaire de renseigner d’autres informations. En revanche, il est intéressant au début de mettre le niveau de log à « Debug » pour avoir la visibilité des trames traitées dans l’onglet Mymodbus des Logs.

Le plugin MyModbus est accessible dans l’onglet plugins > Protocole domotique

Echanger avec sa chaudière

Dans le plugin Mymodbus, nous allons créer la chaudière en cliquant sur « Ajouter » puis en lui donnant un nom.

Arrivé dans l’équipement, il est possible de l’affecter à un objet parent, de le catégoriser et enfin le rendre actif et visible.

Ensuite, nous rentrons dans la configuration qui demande avant tout de choisir le mode de connexion MODBUS que nous utilisons. Il existe plusieurs possibilités offertes par le plugin mais dans notre cas, nous utiliserons TCPIP.

Les champs suivants demandent de saisir l’adresse IP locale du module USR-W610 ainsi que le port saisie dans la configuration (dans mon cas 26). Pour ce qui est de l’Unit ID, il doit être positionné à 10 car c’est l’adresse de la chaudière maitre. Enfin, le Polling peut-être fixé comme on le souhaite, ici à 5 secondes et sans garder la connexion active.

Mais ça ne marche pas ton truc !

Si vous commencez à saisir des commandes pour espérer obtenir des réponses, vous verrez que cela n’est pas stable : parfois la chaudière répond, parfois pas et généralement le démon tombe… Mais pourquoi !?

Comme expliqué plus haut, cela est lié au mode bi-maitre utilisé par De Dietrich qui fait que la chaudière envoi des trames puis se met en sommeil environ toutes les 5 secondes. Là on pourrait se dire qu’il suffirait de fixer le polling à 5 secondes comme nous l’avons fait et de lancer l’échange au bon moment… Oui mais compliqué à caler avec un risque important de voir le démon tomber au bout d’un moment car nous ne nous sommes pas positionnés parfaitement.

Aussi, il a fallut regarder le code du plugin et voir comment se sortir de cette situation.

Modification du code du plugin pour s’en sortir

Lors que l’on regard avec un outil pour envoyer et recevoir des trames, on constate que lorsque nous envoyons un demande pour lire un registre, la chaudière :

  • soit répond correctement,
  • soit retourne n’importe quoi.

Lorsque n’importe quoi est retourné, on s’aperçoit que la trame ne ressemble à rien ce qui génère une erreur… ce qui est intéressant…

Pour aller voir ce que fait le plugin, il faut aller dans l’Editeur de fichiers de Jeedom (Réglages > Système) puis aller dans le répertoire plugins > mymodbus > ressources. Là plusieurs fichiers sont présents dont :

  • celui qui traite de la lecture « mymodbus_demond.py »
  • celui qui traite de l’écriture « mymodbus_write.py »

Pour l’instant, intéressons-nous à la lecture et ouvrons le premier fichier. On remarque tout d’abord que le code est fait par @Bebel27 que je profite pour remercier au passage pour son excellent plugin. Ensuite, nous avons différents imports de librairies et autres vérifications de versions de python…

NOTE
Je ne vais pas décrire la totalité du code du plugin mais tenter d’expliquer un peu les principaux endroits qui méritent notre attention.

Nous avons également la liste des arguments envoyés au plugin lorsque nous émettons une trame dont le protocole utilisé, l’adresse de l’hôte, le port, etc. Nous avons également le type de fonction utilisée pour la lecture dont « Holding register (hrs) » qui nous intéresse (c’est Type Entrée/Sortie qu’il faudra utiliser pour échanger avec la chaudière).

Plus bas, nous voyons l’endroit où le protocol « tcpip » est vérifié puis nous rentrons dans une boucle qui vérifie la fonction utilisée. Nous descendons plus bas jusqu’à voir :

if (args.hrs) != None :

Cette ligne vérifie que l’argument relatif à la fonction est bien hrs comme « Holding register » que nous utilisons.

Là nous avons plusieurs boucles qui permettent de parcourir les différents registres que nous demandons. En effet, le plugin envoi plusieurs demandes en une fois ce qui entraine la construction d’un tableau avec tous nos registres.

List_hrs = (args.hrs).split(',')

Aussi, si nous envoyons en arguments « hrs » les valeurs de registres « 14,15,456 » par exemple, le code reprend ces éléments pour les répartir dans un tableau appelé « List_hrs » construit en utilisant la virgule comme indice de séparation.

La boucle for ensuite permet de parcourir le tableau « List_hrs » en mettant dans la variable table chaque valeur du tableau à chaque tour de boucle.

for table in List_hrs:

Plusieurs « if » s’enchainent ensuite mais nous remarquons plus particulièrement les endroits où la trame est envoyée puis réceptionnée.

rr = client.read_holding_registers(int(hreg_first),i,unit=args.unid)
assert(not rr.isError())     # test that we are not an error
subprocess.Popen(['/usr/bin/php',mymodbus,'add='+args.host,'unit='+str(args.unid),'eqid='+str(args.eqid),'type=holding_registers','sortie=1','inputs='+str(int(hreg_first)),'values='+str(rr.registers)])

Par contre on voir que si une erreur est constatée dans la trame, alors le code s’arrête grâce à la fonction de contrôle d’erreur assert().

ATTENTION
C’est là que nous avons un problème car la chaudière nous retourne n’importe quoi après une demande faite au mauvais moment. Du coup, le code s’arrête et le démon se met en défaut.

Pour contourner ce problème, il faudrait chercher à identifier les zones de silences pour pouvoir communiquer. Ce serait la manière la plus propre… mais je n’ai pas trouvé comment le faire n’ayant pas des compétences fortes en développement.

Aussi, je suis plutôt parti du principe de vérifier si nous avions une erreur et de relancer la requête tant que la trame reçue est incorrecte… Cela multiplie les requêtes mais ça fonctionne et ne met plus le démon en défaut.

Pour faire cela, il suffit de remplacer le code précédent par celui-ci :

while True:
    time.sleep(1)
    rr = client.read_holding_registers(int(hreg_first),i,unit=args.unid)
    if (not rr.isError()):     # test that we are not an error
        break
    #rr = client.read_holding_registers(int(hreg_first),i,unit=args.unid)
    #assert(not rr.isError())     # test that we are not an error
	subprocess.Popen(['/usr/bin/php',mymodbus,'add='+args.host,'unit='+str(args.unid),'eqid='+str(args.eqid),'type=holding_registers','sortie=1','inputs='+str(int(hreg_first)),'values='+str(rr.registers)])

Je rentre donc dans une boucle qui répète toute les secondes la requête et vérifie s’il y a une erreur. Si oui alors on boucle, sinon on sort de la boucle et on traite la réponse. Le code précédent a été mis en commentaire afin de ne pas le perdre.

Il faut donc remplacer cela à tous les endroits du code où se trouve ces requêtes dans la partie « hrs ». Pour faciliter les choses, le code complet est ci-dessous.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Code : Demond Mymobus
date: 17/02/2021
Auteur: @Bebel27
Version: b2.0
"""
import sys
import time
import argparse
import os
import subprocess
from threading import Thread, Lock

# Conversion
from pymodbus.constants import Endian
from pymodbus.payload import BinaryPayloadDecoder
from pymodbus.payload import BinaryPayloadBuilder

#Compatibility
from pymodbus.compat import IS_PYTHON3, PYTHON_VERSION
if IS_PYTHON3 and PYTHON_VERSION >= (3, 4):
    print("Version de python ok")
    
else:
    sys.stderr("merci d'installer Python 3 ou de relancer les dépendances Mymodbus")
    sys.exit(1)



mymodbus = os.path.abspath(os.path.join(os.path.dirname(__file__), '../core/php/mymodbus.inc.php'))

parser = argparse.ArgumentParser(description='Mymodbus values.')
#-----------Générale---------------------------------------------------------------------
parser.add_argument("--verbosity", help="mode debug")
parser.add_argument("--protocol", type=str ,help="Choix protocole Modbus" ,required=True)
parser.add_argument("--host", type=str ,help="Choix de l'adresse host")
parser.add_argument("--port", type=str ,help="Choix du port", required=True)
parser.add_argument("--polling", type=int ,help="polling en s", required=True)
parser.add_argument("--unid", type=int ,help="choix Unit Id", required=True)
parser.add_argument("--keepopen", type=int ,help="Garde la connexion ouverte")
parser.add_argument("--eqid", type=int ,help="Numero equipement Jeedom", required=True)
#------------RTU-----------------------------------------------------------
parser.add_argument("--baudrate", type=int ,help="vitesse de com en bauds")
parser.add_argument("--stopbits", type=int ,help="bit de stop 1 ou 2")
parser.add_argument("--parity", type=int ,help="parity oui ou non ")
parser.add_argument("--bytesize", type=int ,help="Taile du mot 7 ou 8 ")
#-----------Fonctions---------------------------------------------
parser.add_argument("--coils", type=str ,help="Type Coils")
parser.add_argument("--dis", type=str, help="discrete imput")
parser.add_argument("--hrs", type=str ,help="Holding register")
parser.add_argument("--irs", type=str ,help="imput register")
#------------------------------------------------------------------
# Options demandées
#---------------------
parser.add_argument("--virg", type=str ,help="Holding à virgules")
parser.add_argument("--swapi32", type=str ,help="inverse 32bit")
parser.add_argument("--sign", type=str ,help="valeurs signées")

args = parser.parse_args()

#if args.verbosity:
#    print("verbosity turned on")
    


# mymodbus polling thread
def polling_thread():

    if args.protocol == 'rtu':
        from pymodbus.client.sync import ModbusSerialClient as ModbusClient
        client = ModbusClient(method='rtu', port=args.port, timeout=10,stopbits = 1, bytesize = 8, parity = 'N', baudrate= args.baudrate)
    
    if args.protocol == 'tcpip':
        from pymodbus.client.sync import ModbusTcpClient as ModbusClient
        client = ModbusClient(host=args.host, port=args.port, timeout=10)
    
    if args.protocol == 'rtuovertcp':
        from pymodbus.client.sync import ModbusTcpClient as ModbusClient
        from pymodbus.transaction import ModbusRtuFramer as ModbusFramer
        client = ModbusClient(host=args.host, port=args.port, framer=ModbusFramer)
  
    while True:
        client.connect()

        #lecture Discrete_inputs (2)

        if (args.dis) != None :
            List_dis = (args.dis).split(',')
            di_start=List_dis[0]
            i=1
            for di in List_dis:
                if int(di) == int(di_start):
                    di_previous=di_start
                    if int(di) == int(List_dis[-1]):
                        rr = client.read_discrete_inputs(int(di_start),i,unit=args.unid)
                        subprocess.Popen(['/usr/bin/php',mymodbus,'add='+args.host,'unit='+str(args.unid),'eqid='+str(args.eqid),'type=discrete_inputs','sortie=1','inputs='+str(int(di_start)),'values='+str(rr.bits[:i])])
                        print(int(di_start))
                elif int(di) == int(di_previous) + 1 :
                    di_previous=int(di)
                    i += 1
                    if int(di) == int(List_dis[-1]):
                        rr = client.read_discrete_inputs(int(di_start),i,unit=args.unid)
                        subprocess.Popen(['/usr/bin/php',mymodbus,'add='+args.host,'unit='+str(args.unid),'eqid='+str(args.eqid),'type=discrete_inputs','sortie=2','inputs='+str(list(range(int(di_start),int(di_start)+i))),'values='+str(rr.bits[:i])])
                else :
                    if int(di) != int(di_previous):
                        rr = client.read_discrete_inputs(int(di_start),i,unit=args.unid)
                        subprocess.Popen(['/usr/bin/php',mymodbus,'add='+args.host,'unit='+str(args.unid),'eqid='+str(args.eqid),'type=discrete_inputs','sortie=3','inputs='+str(list(range(int(di_start),int(di_start)+i))),'values='+str(rr.bits[:i])])
                        di_start=int(di)
                        di_previous=int(di)
                        i=1
                        if int(di) == int(List_dis[-1]):
                            rr = client.read_discrete_inputs(int(di_start),i,unit=args.unid)
                            subprocess.Popen(['/usr/bin/php',mymodbus,'add='+args.host,'unit='+str(args.unid),'eqid='+str(args.eqid),'type=discrete_inputs','sortie=4','inputs='+str(list(range(int(di_start),int(di_start)+i))),'values='+str(rr.bits[:i])])


        #lecture holding register (3)
                            
        if (args.hrs) != None :
            List_hrs = (args.hrs).split(',')
            hreg_first=List_hrs[0]
            i=1
            for table in List_hrs:
                if int(table) == int(hreg_first):
                    hr_previous=hreg_first
                    if int(table) == int(List_hrs[-1]):
                        while True:
                            time.sleep(1)
                            rr = client.read_holding_registers(int(hreg_first),i,unit=args.unid)
                            if (not rr.isError()):     # test that we are not an error
                                break
                        #rr = client.read_holding_registers(int(hreg_first),i,unit=args.unid)
                        #assert(not rr.isError())     # test that we are not an error
                        subprocess.Popen(['/usr/bin/php',mymodbus,'add='+args.host,'unit='+str(args.unid),'eqid='+str(args.eqid),'type=holding_registers','sortie=1','inputs='+str(int(hreg_first)),'values='+str(rr.registers)])
                elif int(table) == int(hr_previous)+1:
                    hr_previous=int(table)
                    i += 1
                    if int(table) == int(List_hrs[-1]):
                        while True:
                            time.sleep(1)
                            rr = client.read_holding_registers(int(hreg_first),i,unit=args.unid)
                            if (not rr.isError()):     # test that we are not an error
                                break
                        #rr = client.read_holding_registers(int(hreg_first),i,unit=args.unid)
                        #assert(not rr.isError())     # test that we are not an error
                        subprocess.Popen(['/usr/bin/php',mymodbus,'add='+args.host,'unit='+str(args.unid),'eqid='+str(args.eqid),'type=holding_registers','sortie=2','inputs='+str(list(range(int(hreg_first),int(hreg_first)+i))),'values='+str(rr.registers)])
                else :
                    if int(table) != int(hr_previous):
                        while True:
                            time.sleep(1)
                            rr = client.read_holding_registers(int(hreg_first),i,unit=args.unid)
                            if (not rr.isError()):     # test that we are not an error
                                break
                        subprocess.Popen(['/usr/bin/php',mymodbus,'add='+args.host,'unit='+str(args.unid),'eqid='+str(args.eqid),'type=holding_registers','sortie=3','inputs='+str(list(range(int(hreg_first),int(hreg_first)+i))),'values='+str(rr.registers)])
                        hreg_first=int(table)
                        hr_previous=int(table)
                        time.sleep(0.1) #pause pour la pac 
                        i=1
                        if int(table) == int(List_hrs[-1]):
                            while True:
                                time.sleep(1)
                                rr = client.read_holding_registers(int(hreg_first),i,unit=args.unid)
                                if (not rr.isError()):     # test that we are not an error
                                    break
                            #rr = client.read_holding_registers(int(hreg_first),i,unit=args.unid)
                            #assert(not rr.isError())     # test that we are not an error
                            subprocess.Popen(['/usr/bin/php',mymodbus,'add='+args.host,'unit='+str(args.unid),'eqid='+str(args.eqid),'type=holding_registers','sortie=4','inputs='+str(list(range(int(hreg_first),int(hreg_first)+i))),'values='+str(rr.registers)])

        #lecture coils (1)
                            
        if (args.coils) != None :
            List_coils = (args.coils).split(',')
            coil_start=List_coils[0]
            i=1
            for coil in List_coils:
                if int(coil) == int(coil_start):
                    coil_previous=coil_start
                    if int(coil) == int(List_coils[-1]):
                        rr = client.read_coils(int(coil_start),i,unit=args.unid)
                        subprocess.Popen(['/usr/bin/php',mymodbus,'add='+args.host,'unit='+str(args.unid),'eqid='+str(args.eqid),'type=coils','sortie=1','inputs='+str(int(coil_start)),'values='+str(rr.bits[:i])])
                elif int(coil) == int(coil_previous) + 1 :
                    coil_previous=int(coil)
                    i += 1
                    if int(coil) == int(List_coils[-1]):
                        rr = client.read_coils(int(coil_start),i,unit=args.unid)
                        subprocess.Popen(['/usr/bin/php',mymodbus,'add='+args.host,'unit='+str(args.unid),'eqid='+str(args.eqid),'type=coils','sortie=2','inputs='+str(list(range(int(coil_start),int(coil_start)+i))),'values='+str(rr.bits[:i])])
                else :
                    if int(coil) != int(coil_previous):
                        rr = client.read_coils(int(coil_start),i,unit=args.unid)
                        subprocess.Popen(['/usr/bin/php',mymodbus,'add='+args.host,'unit='+str(args.unid),'eqid='+str(args.eqid),'type=coils','sortie=3','inputs='+str(list(range(int(coil_start),int(coil_start)+i))),'values='+str(rr.bits[:i])])
                        coil_start=int(coil)
                        coil_previous=int(coil)
                        i=1
                        if int(coil) == int(List_coils[-1]):
                            rr = client.read_coils(int(coil_start),i,unit=args.unid)
                            subprocess.Popen(['/usr/bin/php',mymodbus,'add='+args.host,'unit='+str(args.unid),'eqid='+str(args.eqid),'type=coils','sortie=4','inputs='+str(list(range(int(coil_start),int(coil_start)+i))),'values='+str(rr.bits[:i])])

        #lecture input registers
                            
        if (args.irs) != None :
            List_irs = (args.irs).split(',')
            ir_start=List_irs[0]
            i=1
            for ir in List_irs:
                if int(ir) == int(ir_start):
                    ir_previous=ir_start
                    if int(ir) == int(List_irs[-1]):
                        rr = client.read_input_registers(int(ir_start),i,unit=args.unid)
                        #assert(not rr.isError()
                        subprocess.Popen(['/usr/bin/php',mymodbus,'add='+args.host,'unit='+str(args.unid),'eqid='+str(args.eqid),'type=input_registers','sortie=1','inputs='+str(int(ir_start)),'values='+str(rr.registers)])
                elif int(ir) == int(ir_previous) + 1 :
                    ir_previous=int(ir)
                    i += 1
                    if int(ir) == int(List_irs[-1]):
                        rr = client.read_input_registers(int(ir_start),i,unit=args.unid)
                        subprocess.Popen(['/usr/bin/php',mymodbus,'add='+args.host,'unit='+str(args.unid),'eqid='+str(args.eqid),'type=input_registers','sortie=2','inputs='+str(list(range(int(ir_start),int(ir_start)+i))),'values='+str(rr.registers)])
                else :
                    if int(ir) != int(ir_previous):
                        rr = client.read_input_registers(int(ir_start),i,unit=args.unid)
                        subprocess.Popen(['/usr/bin/php',mymodbus,'add='+args.host,'unit='+str(args.unid),'eqid='+str(args.eqid),'type=input_registers','sortie=3','inputs='+str(list(range(int(ir_start),int(ir_start)+i))),'values='+str(rr.registers)])
                        ir_start=int(ir)
                        ir_previous=int(ir)
                        i=1
                        if int(ir) == int(List_irs[-1]):
                            rr = client.read_input_registers(int(ir_start),i,unit=args.unid)
                            subprocess.Popen(['/usr/bin/php',mymodbus,'add='+args.host,'unit='+str(args.unid),'eqid='+str(args.eqid),'type=input_registers','sortie=4','inputs='+str(list(range(int(ir_start),int(ir_start)+i))),'values='+str(rr.registers)])


        #lecture des valeurs signées
                            
        if (args.sign) != None :
            List_sign = (args.sign).split(',')
            i = 1
            int_first=List_sign[0]
            for sign_16 in List_sign:
                rr = client.read_holding_registers(int(sign_16),i,unit=args.unid)
                #assert(not rr.isError())
                decoder = BinaryPayloadDecoder.fromRegisters(rr.registers,byteorder=Endian.Big,wordorder=Endian.Little)
                #print (int (decoder.decode_16bit_int()))
                subprocess.Popen(['/usr/bin/php',mymodbus,'add='+args.host,'unit='+str(args.unid),'eqid='+str(args.eqid),'type=sign','sortie=1','inputs='+str(int(sign_16)),'values='+str(int(decoder.decode_16bit_int()))])

        #lecture des valeurs à virgules

        if (args.virg) != None :
            List_virg = (args.virg).split(',')
            i= 2   
            virg_first=List_virg[0]
            for virg_reg in List_virg:
                rr = client.read_holding_registers(int(virg_reg),i,unit=args.unid)
                decoder = BinaryPayloadDecoder.fromRegisters(rr.registers,byteorder=Endian.Big,wordorder=Endian.Little)
                subprocess.Popen(['/usr/bin/php',mymodbus,'add='+args.host,'unit='+str(args.unid),'eqid='+str(args.eqid),'type=virg','sortie=1','inputs='+str(int(virg_reg)),'values='+str(float(round(decoder.decode_32bit_float(),2)))])

        #lecture des imputregisters swapées

        if (args.swapi32) != None :
            List_swapi32 = (args.swapi32).split(',')
            i= 2   
            swapi32_first=List_swapi32[0]
            for swapi32_reg in List_swapi32:
                rr = client.read_input_registers(int(swapi32_reg),i,unit=args.unid)
                decoder = BinaryPayloadDecoder.fromRegisters(rr.registers,byteorder=Endian.Big,wordorder=Endian.Big)
                subprocess.Popen(['/usr/bin/php',mymodbus,'add='+args.host,'unit='+str(args.unid),'eqid='+str(args.eqid),'type=swapi32','sortie=1','inputs='+str(int(swapi32_reg)),'values='+str(float(round(decoder.decode_32bit_float(),2)))])

        # ----------------------------------------------------------------------- #
        # close the client
        # ----------------------------------------------------------------------- #
        if args.keepopen == 0 :
            client.close()
        time.sleep(args.polling)
        
    
# start polling thread
t = Thread(target=polling_thread)
# set demond
t.daemon = True
t.start()

if __name__ == '__main__':
    
    while True:
        if t.is_alive():
            pass
            #print("Thread_Ok")
        else:
            #print("thread_Ko")
            raise ParameterException('Thread en défaut')
        time.sleep(1)

Il suffit d’en faire un fichier mymodbus_demond-.py et de remplacer celui du plugin. Le mieux est de sauvegarder quelque part le précédent en cas de souci pour y revenir facilement.

ATTENTION
Si vous modifiez vous même le fichier, prenez garde à l’indentation car le langage Python y est très sensible. Il est souvent préférable d’utiliser les 4 espaces et non une tabulation.

Pour la partie écriture, c’est exactement le même principe. Le code complet est ci-dessous :

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Code : Write Mymobus
date: 27/02/2021
Auteur: @Bebel27
Version: b2.0
"""

from pymodbus.compat import IS_PYTHON3, PYTHON_VERSION
if IS_PYTHON3 and PYTHON_VERSION >= (3, 4):
    print("Version de python ok")
    
else:
    sys.stderr("merci d'installer Python 3 ou de relancer les dépendances Mymodbus")
    sys.exit(1)

import time
import sys
import argparse
import os
import subprocess

parser = argparse.ArgumentParser(description='Mymodbus Write')
#-----------Générale---------------------------------------------------------------------
parser.add_argument("--verbosity", help="mode debug")
parser.add_argument("--protocol", type=str ,help="Choix protocole Modbus" ,required=True)
parser.add_argument("--host", type=str ,help="Choix de l'adresse host")
parser.add_argument("--port", type=str ,help="Choix du port", required=True)
parser.add_argument("--unid", type=int ,help="choix Unit Id", required=True)
parser.add_argument("--eqid", type=int ,help="Numero equipement Jeedom")
#------------RTU-----------------------------------------------------------
parser.add_argument("--baudrate", type=int ,help="vitesse de com en bauds")
parser.add_argument("--stopbits", type=int ,help="bit de stop 1 ou 2")
parser.add_argument("--parity", type=int ,help="parity oui ou non ")
parser.add_argument("--bytesize", type=int ,help="Taile du mot 7 ou 8 ")
#-----------Fonctions---------------------------------------------
parser.add_argument("--wsc", type=int ,help="Write single Coil")
parser.add_argument("--whr", type=int, help="Write holding register")
parser.add_argument("--wmhr", type=int ,help="Write multiple holdings registers")
parser.add_argument("--value", type=int ,help="value")
#------------------------------------------------------------------
# Options demandées
#---------------------
#parser.add_argument("--virg", type=str ,help="Holding à virgules")
#parser.add_argument("--swapi32", type=str ,help="inverse 32bit")
#parser.add_argument("--sign", type=str ,help="valeurs signées")

args = parser.parse_args()

#if args.verbosity:
#    print("verbosity turned on")
    
if args.protocol == 'rtu':
    from pymodbus.client.sync import ModbusSerialClient as ModbusClient
    #client = ModbusClient(method='rtu', port=args.port, timeout=1,baudrate=38400)
    client = ModbusClient(method='rtu', port=args.port, timeout=1,stopbits = 1, bytesize = 8, parity = 'N', baudrate= args.baudrate)
    
if args.protocol == 'tcpip':
    from pymodbus.client.sync import ModbusTcpClient as ModbusClient
    client = ModbusClient(host=args.host, port=args.port,retries=3, retry_on_empty=True)
    
if args.protocol == 'rtuovertcp':
    from pymodbus.client.sync import ModbusTcpClient as ModbusClient
    from pymodbus.transaction import ModbusRtuFramer as ModbusFramer
    client = ModbusClient(host=args.host, port=args.port, framer=ModbusFramer)


    
#if not client.connect():
#    print("unable to connect to "+host+":"+str(port))

client.connect()

if (args.wsc) != None :
    if (args.value) == 1:
        val = True
    if (args.value) == 0 :
        val = False
    rq = client.write_coil(args.wsc, val, unit=args.unid)
    assert(not rq.isError())     # test that we are not an error
        
if (args.whr) != None :
    while True:
        time.sleep(1)
        rq = client.write_register(args.whr, args.value, unit=args.unid)
        if (not rq.isError()):     # test that we are not an error
            break
    #rq = client.write_register(args.whr, args.value, unit=args.unid)
    #assert(not rq.isError())     # test that we are not an error
        
if (args.wmhr) != None :
    while True:
        time.sleep(1)
        rq = client.write_registers(args.wmhr, args.value, unit=args.unid)
        if (not rq.isError()):     # test that we are not an error
            break
    #rq = client.write_registers(args.wmhr, args.value, unit=args.unid)
        
client.close()

Créer les commandes utiles

Nous y sommes, maintenant que toutes les modifications et la configuration ont été réalisées, il est temps de créer les commandes dans le plugin MyModbus pour récupérer les informations utiles ou changer des paramètres. Le tableau suivant décrit succinctement les différents champs selon que l’on lit ou que l’on écrit.

Champ Cas lecture Cas Ecriture
Nom Le nom de la commande Le nom de la commande
Type Info/Numérique Action/Défaut
Type E/S Holding Register / 16 bits Write Multiple Holding
Adresse Le registre à lire Le registre à écrire
Paramètre(s) vide valeur utile
Options l’unité de la commande rien
Pour illustrer, voici quelques exemples de lecture et écriture dans Jeedom :

Le plugin va prendre chaque registre et l’envoyer à la chaudière pour récupérer les informations ou modifier les paramètres demandés.

NOTE
Le plugin peut prendre plus ou moins de temps à mettre à jour les informations ou changer les paramètres. Cela est lié au « moment d’écoute » de la chaudière.

ATTENTION
Il est aussi possible que la commande que vous demandez ne soit pas correcte. En l’état, la modification que nous avons effectuée dans le code fera recommencer indéfiniment la requête sans s’arrêter. Il est donc important quand vous entrez une nouvelle commande de vérifier que vous avez bien un retour. Si ce n’es pas le cas, il faut la supprimer puis sauvegarder ce qu devrait faire repartir le démon et donc sortir de la boucle.

Conclusion

En espérant que cela vous soit utile pour créer ensuite un joli widget qui permet de contrôler la chaudière et changer ses paramètres ! N’hésitez pas à mettre vos créations !

Personnellement, j’ai utilisé le plugin Virtuel pour créer des commandes de consignes et je tente de piloter la chaudière avec les modes selon la température intérieure que je récupère via un capteur Xiaomi. Je voulais éviter le fonctionnement par arrêt et lancement de la chaudière dès l’atteinte de la température pour profiter malgré tout de la condensation. Cela remplace en quelque sorte la sonde d’ambiance que ne j’ai pas.

Pour l’instant c’est en test afin de voir si c’est pertinent ou non. Je n’ai pas assez de recule ou de connaissance du fonctionnement de la chaudière pour me faire une idée claire. Notamment, la chaudière se cale sur une température calculée automatiquement selon la température extérieure et les consignes. Je ne comprends pas bien encore aujourd’hui ce mode de calcule…

Bonus pour gérer l’affichage des températures extérieures négatives

Lorsque la chaudière est interrogée sur la température extérieure et que cette dernière est négative, une valeur supérieure à 3200 est retournée alors que les valeurs positives sont justes… Embêtant me direz-vous !

Pour régler ce souci, il suffit de créer une commande information dans un virtuel de la manière suivante :

  1. Après avoir créé le virtuel (ou avoir utilisé un existant), créer une nouvelle commande info.
  2. Mettre le nom souhaité à la place de TEMP_EXT sur mon exemple.
  3. Laisser la commande en Numérique.
  4. Saisir ensuite la formule suivante :
    (#[VOTRE][COMMANDE][TEMPERATURE_CHAUDIERE]# < 3200) ? #[VOTRE][COMMANDE][TEMPERATURE_CHAUDIERE]# : -(#[VOTRE][COMMANDE][TEMPERATURE_CHAUDIERE]# - 3276.8)
    On vérifie donc si la température remontée par la chaudière dépasse les 3200, si pas on affiche la valeur remontée, si elle dépasse c’est que nous sommes en présence d’une température négative donc nous faisons un calcul pour l’afficher correctement (la valeur 3276.8 utilisée vient de recherches sur le Web mais je ne peux pas l’expliquer concrètement).
  5. Ajouter dans le champ unité sur la droite °C.
  6. La valeur s’affichant avec beaucoup de chiffres après la virgule, en cliquant sur le bouton paramètre (les roues crantées sut la droite de la commande) puis sur l’onglet configuration, on voit le champ Arrondi dans lequel on peut précise le nombre de chiffres après la virgule que l’on veut voir afficher (1, 2, ou plus selon ses envies).

ATTENTION : Cette astuce ne corrige pas la valeur remontée par la chaudière. Il faut donc bien utiliser la commande virtuelle que nous venons de créer pou avoir la bonne valeur de température extérieure.

J’en profite pour remercier @loustic03 pour ses tests de donc fonctionnement de cette astuce ! :+1:

2 « J'aime »

Hello,
Super tuto, j’ai abandonner le plugin car justement le démon tombe en permanence et beaucoup d’erreurs de remonter d’info comme tu le précise .
Je vais peut être remettre en service le plugin.

Oui la chaudière recalcule en temps réel ( a peu prés toutes les 6 secondes, remonter d’info de la sonde extérieur) la température de départ avec la loi d’eau plus les consignes.

Merci pour ton retour :+1:

J’avais vu effectivement cette histoire de loi d’eau mais je n’ai pas encore creusé sur la manière avec laquelle la chaudière fait son calcul.

T° eau = 1.5 x (T° intérieure demandée - T° extérieure) + 23.

Du coup j’aurais avec la capture ci-dessus : T° eau = 1.5 x (18° - 10.9°) + 23 = 33,65°
Mais je ne retrouve ça nul part…

Actuellement, je mets une consigne JOUR et une consigne NUIT comme on voit sur la capture et j’utilise un capteur pour récupérer ma température intérieure. Ensuite, j’ai un scénario qui se déclenche à chaque fois que la température du capteur change pour passer en mode JOUR ou NUIT selon que je dépasse de 0.1°C mas consigne en plus ou moins. J’ai la même chose la nuit mais en réduisant la température de consigne de 3°C.

Ça marche à peu prêt mais j’ai peur pour l’instant que cela soit biaisé car la température extérieure est clémente en journée en ce moment… Et je me dis qu’il y a peut être une meilleure méthode en modifiant aussi la consigne pour que la température calculée soit plus cohérente…

la chaudière calcule la temperature soit réel c’est a dire quelle a les 2 infos ( temp EXT et temp intérieure) avec soit une prise en compte de la température intérieure en influence direct, soit avec une influence modérer ou influence légère.
Ou elle calcule uniquement avec la sonde EXT et la consigne défini sur circuit A , B ou C
Perso je suis en sonde EXT et sonde intérieur avec prise en compte modérer.

Je viens de remettre le plugin mais pas moyen d’installé les dépendances, j’ai renommer les 2 fichiers mymodbus_demond.py et mymodbus_write.py en .pia ensuite j’ai mis les 2 nouveaux fichiers mymodbus_demond.py et mymodbus_write.py et coller tes codes dans chaque fichier ensuite j’ai réactiver le plugin

Sur une partie de mon hydraulique j’utilise ma pente

#[test chauffage][Pente chauffage][Etat]#*(#[test chauffage][Pente chauffage][Etat_cons]#-#[Extérieur météo ][Température Ext-shelly][Temp Ext]#)+#[Salon][Température salon][Température]#

Qui correspond a

T° eau =(pente) 0.5 x (T° intérieure demandée - T° extérieure) + 23 (température intérieure).

Merci pour ces précisions, il faut que je regarde à tête reposer :wink:

Pour le plugin c’est étrange. Que te disent les logs en mode DEBUG ?

Personnellement, j’ai installé le plugin en beta puis installé les dépendances. Une fois fait, j’ai modifié le fichier. Il faut faire attention avec les fichiers de bien vérifier les intentions. Le mieux je pense est de modifier le code des fichiers existants en prenant exemple sur les miens…

Le log

++++++++++++++++++++++++++++++++++++++
+  MyModbus Install dependancies
+  v1.5
+  By Bebel27
++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MyModbus - Debut de l'installation des dependances ...
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
lundi 14 novembre 2022, 20:30:44 (UTC+0100)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mises a jour du systeme en cours ...
/!\ Peut etre long suivant l'anciennete de votre systeme.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Installation dependance  python-pip
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Lecture des listes de paquets…
Construction de l'arbre des dépendances…
Lecture des informations d'état…
python-setuptools est déjà la version la plus récente (40.8.0-1).
python3-setuptools est déjà la version la plus récente (40.8.0-1).
python-pip est déjà la version la plus récente (18.1-5+rpt1).
python3-pip est déjà la version la plus récente (18.1-5+rpt1).
Le paquet suivant a été installé automatiquement et n'est plus nécessaire :
raspi-gpio
Veuillez utiliser « sudo apt autoremove » pour le supprimer.
0 mis à jour, 0 nouvellement installés, 0 à enlever et 0 non mis à jour.
-
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Installation dependance  pypModbus
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting pyModbus==2.5.3
Using cached https://www.piwheels.org/simple/pymodbus/pymodbus-2.5.3-py2.py3-none-any.whl (154 kB)
Requirement already satisfied: pyserial>=3.4 in /usr/local/lib/python3.7/dist-packages (from pyModbus==2.5.3) (3.5)
Requirement already satisfied: six>=1.15.0 in /usr/local/lib/python3.7/dist-packages (from pyModbus==2.5.3) (1.16.0)
ERROR: Could not install packages due to an OSError: [Errno 2] Aucun fichier ou dossier de ce type: '/usr/local/lib/python3.7/dist-packages/pyserial-3.5.dist-info/METADATA'
--- Logging error ---
Traceback (most recent call last):
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/utils/logging.py", line 177, in emit
self.console.print(renderable, overflow="ignore", crop=False, style=style)
File "/usr/local/lib/python3.7/dist-packages/pip/_vendor/rich/console.py", line 1752, in print
extend(render(renderable, render_options))
File "/usr/local/lib/python3.7/dist-packages/pip/_vendor/rich/console.py", line 1390, in render
for render_output in iter_render:
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/utils/logging.py", line 134, in __rich_console__
for line in lines:
File "/usr/local/lib/python3.7/dist-packages/pip/_vendor/rich/segment.py", line 245, in split_lines
for segment in segments:
File "/usr/local/lib/python3.7/dist-packages/pip/_vendor/rich/console.py", line 1368, in render
renderable = rich_cast(renderable)
File "/usr/local/lib/python3.7/dist-packages/pip/_vendor/rich/protocol.py", line 36, in rich_cast
renderable = cast_method()
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/self_outdated_check.py", line 130, in __rich__
pip_cmd = get_best_invocation_for_this_pip()
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/utils/entrypoints.py", line 60, in get_best_invocation_for_this_pip
os.path.join(binary_prefix, exe_name),
File "/usr/lib/python3.7/genericpath.py", line 97, in samefile
s2 = os.stat(f2)
FileNotFoundError: [Errno 2] Aucun fichier ou dossier de ce type: '/usr/bin/pip3.7'
Call stack:
File "/usr/local/bin/pip3", line 10, in 
sys.exit(main())
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/cli/main.py", line 70, in main
return command.main(cmd_args)
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/cli/base_command.py", line 101, in main
return self._main(args)
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/cli/base_command.py", line 223, in _main
self.handle_pip_version_check(options)
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/cli/req_command.py", line 148, in handle_pip_version_check
pip_self_version_check(session, options)
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/self_outdated_check.py", line 237, in pip_self_version_check
logger.info("[present-rich] %s", upgrade_prompt)
File "/usr/lib/python3.7/logging/__init__.py", line 1383, in info
self._log(INFO, msg, args, **kwargs)
File "/usr/lib/python3.7/logging/__init__.py", line 1519, in _log
self.handle(record)
File "/usr/lib/python3.7/logging/__init__.py", line 1529, in handle
self.callHandlers(record)
File "/usr/lib/python3.7/logging/__init__.py", line 1591, in callHandlers
hdlr.handle(record)
File "/usr/lib/python3.7/logging/__init__.py", line 905, in handle
self.emit(record)
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/utils/logging.py", line 179, in emit
self.handleError(record)
Message: '[present-rich] %s'
Arguments: (UpgradePrompt(old='22.1.2', new='22.3.1'),)
-
-
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Installation dependance git
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Lecture des listes de paquets…
Construction de l'arbre des dépendances…
Lecture des informations d'état…
git est déjà la version la plus récente (1:2.20.1-2+deb10u4).
Le paquet suivant a été installé automatiquement et n'est plus nécessaire :
raspi-gpio
Veuillez utiliser « sudo apt autoremove » pour le supprimer.
0 mis à jour, 0 nouvellement installés, 0 à enlever et 0 non mis à jour.
-
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Clonage de rien
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Controle version...
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Version de python
Python 3.7.3
Version de PIP
pip 22.1.2 from /usr/local/lib/python3.7/dist-packages/pip (python 3.7)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Fin de l'installation des dependances MyModbus...
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

En stable elle s’installe mais en beta pas moyen et je suis a jour le py

Perso j’ai les mêmes erreurs mais ça fonctionne quand même !

++++++++++++++++++++++++++++++++++++++
+  MyModbus Install dependancies
+  v1.3
+  By Bebel27
++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MyModbus - Debut de l'installation des dependances ...
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
Wed Nov  2 23:45:35 GMT 2022
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mises a jour du systeme en cours ...
/!\ Peut etre long suivant l'anciennete de votre systeme.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Installation dependance  python-pip
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Reading package lists...
Building dependency tree...
Reading state information...
python3-pip is already the newest version (18.1-5+rpt1).
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
-
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Installation dependance  pypModbusTCP
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Requirement already satisfied: pyModbus in /usr/local/lib/python3.7/dist-packages (2.5.3)
Requirement already satisfied: pyserial>=3.4 in /usr/local/lib/python3.7/dist-packages (from pyModbus) (3.5)
Requirement already satisfied: six>=1.15.0 in /usr/local/lib/python3.7/dist-packages (from pyModbus) (1.16.0)
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
--- Logging error ---
Traceback (most recent call last):
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/utils/logging.py", line 177, in emit
self.console.print(renderable, overflow="ignore", crop=False, style=style)
File "/usr/local/lib/python3.7/dist-packages/pip/_vendor/rich/console.py", line 1673, in print
extend(render(renderable, render_options))
File "/usr/local/lib/python3.7/dist-packages/pip/_vendor/rich/console.py", line 1305, in render
for render_output in iter_render:
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/utils/logging.py", line 134, in __rich_console__
for line in lines:
File "/usr/local/lib/python3.7/dist-packages/pip/_vendor/rich/segment.py", line 249, in split_lines
for segment in segments:
File "/usr/local/lib/python3.7/dist-packages/pip/_vendor/rich/console.py", line 1283, in render
renderable = rich_cast(renderable)
File "/usr/local/lib/python3.7/dist-packages/pip/_vendor/rich/protocol.py", line 36, in rich_cast
renderable = cast_method()
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/self_outdated_check.py", line 130, in __rich__
pip_cmd = get_best_invocation_for_this_pip()
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/utils/entrypoints.py", line 60, in get_best_invocation_for_this_pip
os.path.join(binary_prefix, exe_name),
File "/usr/lib/python3.7/genericpath.py", line 97, in samefile
s2 = os.stat(f2)
FileNotFoundError: [Errno 2] No such file or directory: '/usr/bin/pip'
Call stack:
File "/usr/local/bin/pip", line 8, in 
sys.exit(main())
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/cli/main.py", line 70, in main
return command.main(cmd_args)
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/cli/base_command.py", line 101, in main
return self._main(args)
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/cli/base_command.py", line 223, in _main
self.handle_pip_version_check(options)
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/cli/req_command.py", line 190, in handle_pip_version_check
pip_self_version_check(session, options)
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/self_outdated_check.py", line 236, in pip_self_version_check
logger.warning("[present-rich] %s", upgrade_prompt)
File "/usr/lib/python3.7/logging/__init__.py", line 1395, in warning
self._log(WARNING, msg, args, **kwargs)
File "/usr/lib/python3.7/logging/__init__.py", line 1519, in _log
self.handle(record)
File "/usr/lib/python3.7/logging/__init__.py", line 1529, in handle
self.callHandlers(record)
File "/usr/lib/python3.7/logging/__init__.py", line 1591, in callHandlers
hdlr.handle(record)
File "/usr/lib/python3.7/logging/__init__.py", line 905, in handle
self.emit(record)
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/utils/logging.py", line 179, in emit
self.handleError(record)
Message: '[present-rich] %s'
Arguments: (UpgradePrompt(old='22.2.2', new='22.3'),)
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Requirement already satisfied: pyModbusTCP in /usr/local/lib/python3.7/dist-packages (0.2.0)
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
--- Logging error ---
Traceback (most recent call last):
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/utils/logging.py", line 177, in emit
self.console.print(renderable, overflow="ignore", crop=False, style=style)
File "/usr/local/lib/python3.7/dist-packages/pip/_vendor/rich/console.py", line 1673, in print
extend(render(renderable, render_options))
File "/usr/local/lib/python3.7/dist-packages/pip/_vendor/rich/console.py", line 1305, in render
for render_output in iter_render:
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/utils/logging.py", line 134, in __rich_console__
for line in lines:
File "/usr/local/lib/python3.7/dist-packages/pip/_vendor/rich/segment.py", line 249, in split_lines
for segment in segments:
File "/usr/local/lib/python3.7/dist-packages/pip/_vendor/rich/console.py", line 1283, in render
renderable = rich_cast(renderable)
File "/usr/local/lib/python3.7/dist-packages/pip/_vendor/rich/protocol.py", line 36, in rich_cast
renderable = cast_method()
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/self_outdated_check.py", line 130, in __rich__
pip_cmd = get_best_invocation_for_this_pip()
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/utils/entrypoints.py", line 60, in get_best_invocation_for_this_pip
os.path.join(binary_prefix, exe_name),
File "/usr/lib/python3.7/genericpath.py", line 97, in samefile
s2 = os.stat(f2)
FileNotFoundError: [Errno 2] No such file or directory: '/usr/bin/pip'
Call stack:
File "/usr/local/bin/pip", line 8, in 
sys.exit(main())
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/cli/main.py", line 70, in main
return command.main(cmd_args)
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/cli/base_command.py", line 101, in main
return self._main(args)
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/cli/base_command.py", line 223, in _main
self.handle_pip_version_check(options)
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/cli/req_command.py", line 190, in handle_pip_version_check
pip_self_version_check(session, options)
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/self_outdated_check.py", line 236, in pip_self_version_check
logger.warning("[present-rich] %s", upgrade_prompt)
File "/usr/lib/python3.7/logging/__init__.py", line 1395, in warning
self._log(WARNING, msg, args, **kwargs)
File "/usr/lib/python3.7/logging/__init__.py", line 1519, in _log
self.handle(record)
File "/usr/lib/python3.7/logging/__init__.py", line 1529, in handle
self.callHandlers(record)
File "/usr/lib/python3.7/logging/__init__.py", line 1591, in callHandlers
hdlr.handle(record)
File "/usr/lib/python3.7/logging/__init__.py", line 905, in handle
self.emit(record)
File "/usr/local/lib/python3.7/dist-packages/pip/_internal/utils/logging.py", line 179, in emit
self.handleError(record)
Message: '[present-rich] %s'
Arguments: (UpgradePrompt(old='22.2.2', new='22.3'),)
-
-
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Installation dependance git
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Reading package lists...
Building dependency tree...
Reading state information...
git is already the newest version (1:2.20.1-2+deb10u4).
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
-
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Clonage de mbtget
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Fin de l'installation des dependances MyModbus...
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Version de python
Python 2.7.16
Version de PIP
pip 22.2.2 from /usr/local/lib/python3.7/dist-packages/pip (python 3.7)
-- test install --
OK

Essaie peut-être de lancer des commandes pour voir ce qu’il se passe…

Pour la formule que tu indiques

J’ai demandé la pente à ma chaudière et elle me retourne 20. J’imagine que ça doit correspondre à 2. Par contre quand je calcule j’ai : 2 x 0.5 x (18 - 10,9) + 23 x 21 ce qui fait 490.1 !

Oui ça doit être 2 , mais si c’est le cas tu as une pente un peu élever, pour comfirmer regarde direct sur la chaudière pour voir a combien elle est.
C’est quel diematic que tu as?

Je sais pas quel type de commandes :thinking:
Pour mon problème avec le plugin si tu veux je te MP pour pas pourrir ton tuto

c’est quoi ton calcule?
2x(18-10.9)+23= 37.2° de température de départ