commit e0f031818032a32882592ff27c3150b2a383808e Author: gilles Date: Fri Jan 3 13:32:52 2025 +0100 Initial commit diff --git a/Dockerfile b/Dockerfile new file mode 100755 index 0000000..45b3a0a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +# Utilisez une image de base Python officielle +FROM python:3.9-slim + +# Définissez le répertoire de travail dans le conteneur +WORKDIR /usr/src/app + +# Copiez le fichier des exigences dans le conteneur +COPY requirements.txt ./ + +# Installez les dépendances +RUN pip install --no-cache-dir -r requirements.txt + +# Copier seulement le fichier main.py et config.yaml dans le conteneur +COPY ./src/main.py /usr/src/app/main.py +COPY ./config/config.yaml /usr/src/app/config.yaml + +# Commande pour exécuter l'application +CMD ["python", "./main.py"] \ No newline at end of file diff --git a/config/config.yaml b/config/config.yaml new file mode 100755 index 0000000..84a10e6 --- /dev/null +++ b/config/config.yaml @@ -0,0 +1,467 @@ +# Digital Outputs +# Function: Read Coil Status (FC=01) +# Address Range: 00001-00158 + +# Digital Inputs +# Function: Read Input Status (FC=02) +# Address Range: 10001-10086 + +# Actual Values +# Function: Read Input Registers(FC=04) +# Address Range: 30001-30272 +# input_type : input + +# Parameters +# Function: Read Holding Registers(FC=03) +# Address Range: 40001-41094 +# input_type: holding + + +mqtt: + host: "10.0.0.3" + port: 1883 + user: "" + password: "" + topic_prefix: "froling/S3Turbo" + +homeassistant: + autodiscovery: True + discovery_prefix: "homeassistant" # Préfixe par défaut pour l'autodiscovery + node_id: "froling" # Remplacer par l'identifiant unique du dispositif + +modbus: + host: "10.0.0.12" + port: 502 + unit_id: 2 + timeout: 30 + +refresh_rate: 10 # Fréquence d'actualisation en secondes + +device: + identifiers: "FrolingS3" # Remplacer par l'identifiant unique du dispositif + manufacturer: "Froling" # Remplacer par le fabricant du dispositif + model: "S3" # Remplacer par le modèle du dispositif + name: "Froling S3" # Remplacer par le nom du dispositif + sw_version: "1.0" # Remplacer par la version du logiciel du dispositif, si applicable + # configuration_url: "http://example.com" # Remplacer par l'URL de configuration ou de documentation du dispositif + + +# Ajout des paramètres pour l'état de l'installation et l'état de la chaudière +value_maps: + SystemStatus: + 0: "Charge continue" + 1: "Eau chaude sanitaire" + 2: "Automatique" + 3: "Fonctionnement au bois de chauffage" + 4: "Nettoyage" + 5: "Éteint" + 6: "Chauffage supplémentaire" + 7: "Ramoneur" + 8: "Nettoyage2" + FurnaceStatus: + 0: "DÉFAUT" + 1: "Chaudière éteinte" + 2: "Montée en température" + 3: "Chauffage" + 4: "Maintien du feu" + 5: "Feu éteint" + 6: "Porte ouverte" + 7: "Préparation" + 8: "Préchauffage" + 9: "Allumage" + 10: "Attente d'arrêt" + 11: "Attente d'arrêt1" + 12: "Arrêt_Alimentation1" + 13: "Arrêt_Attente2" + 14: "Arrêt_Alimentation2" + 15: "Nettoyage" + 16: "Attente_2h" + 17: "Aspiration_Chauffage" + 18: "Défaut_d'allumage" + 19: "Prêt_à_l'emploi" + +entities: + - name: "Etats systeme" + unique_id: "etats_systeme_sensor" + type: "sensor" + #device_class: "enum" + #state_class: "measurement" + icon: "mdi:radiator" + unit_of_measurement: "" + state_topic: "froling/S3Turbo/ETATS_SYSTEME/state" + value_template: "{{ value_json.etats_systeme_sensor }}" + input_type: "input_register" + address: 34001 + offset: 30001 + scale: 1 + precision: 1 + value_map: "SystemStatus" + signed: false + - name: "Etat Chaudiere" + unique_id: "etats_chaudiere_sensor" + type: "sensor" + #device_class: "enum" + #state_class: "measurement" + icon: "mdi:water-boiler-alert" + unit_of_measurement: "" + state_topic: "froling/S3Turbo/ETATS_CHAUDIERE/state" + value_template: "{{ value_json.etats_chaudiere_sensor }}" + input_type: "input_register" + address: 34002 + offset: 30001 + scale: 1 + precision: 0 + value_map: "FurnaceStatus" + signed: false + - name: "T° Chaudiere" + unique_id: "temperature_chaudiere_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:hydraulic-oil-temperature" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/TEMPERATURE_CHAUDIERE/state" + value_template: "{{ value_json.temperature_chaudiere_sensor }}" + input_type: "input_register" + address: 30001 + offset: 30001 + scale: 0.5 + precision: 0 + value_map: null + signed: false + - name: "T° Fumee" + unique_id: "temperature_fumee_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:smoke" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/TEMPERATURE_FUMEE/state" + value_template: "{{ value_json.temperature_fumee_sensor }}" + input_type: "input_register" + address: 30002 + offset: 30001 + scale: 1 + precision: 0 + value_map: null + signed: false + - name: "T° Board" + unique_id: "temperature_board_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:thermometer-lines" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/TEMPERATURE_BOARD/state" + value_template: "{{ value_json.temperature_board_sensor }}" + input_type: "input_register" + address: 30003 + offset: 30001 + scale: 0.5 + precision: 0 + value_map: null + signed: false + - name: "O2 residuel" + unique_id: "o2_residuel_sensor" + type: "sensor" + device_class: "battery" + state_class: "measurement" + icon: "mdi:percent" + unit_of_measurement: "%" + state_topic: "froling/S3Turbo/O2_RESIDUEL/state" + value_template: "{{ value_json.o2_residuel_sensor }}" + input_type: "input_register" + address: 30004 + offset: 30001 + scale: 0.1 + precision: 0 + value_map: null + signed: false + - name: "T° Exterieur" + unique_id: "temp_exterieur_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:home-thermometer-outline" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/TEMP_EXTERIEUR/state" + value_template: "{{ value_json.temp_exterieur_sensor }}" + input_type: "input_register" + address: 30005 + offset: 30001 + scale: 0.5 + precision: 0 + value_map: null + signed: true + - name: "Porte chaudiere" + unique_id: "porte_chaudiere_sensor" + type: "binary_sensor" + device_class: "door" + payload_on: "1" + payload_off: "0" + #state_class: "measurement" + icon: "mdi:door-open" + #unit_of_measurement: "" + state_topic: "froling/S3Turbo/PORTE_CHAUDIERE/state" + value_template: "{{ value }}" + input_type: "input_status" + address: 10001 + offset: 10001 + value_map: null + signed: false + + + - name: "Air primaire" + unique_id: "air_primaire_sensor" + type: "sensor" + device_class: "battery" + state_class: "measurement" + icon: "mdi:air-filter" + unit_of_measurement: "%" + state_topic: "froling/S3Turbo/AIR_PRIMAIRE/state" + value_template: "{{ value_json.air_primaire_sensor }}" + input_type: "input_register" + address: 30006 + offset: 30001 + precision: 0 + value_map: null + signed: false + - name: "Air Secondaire" + unique_id: "air_secondaire_sensor" + type: "sensor" + device_class: "Battery" + state_class: "measurement" + icon: "mdi:air-filter" + unit_of_measurement: "%" + state_topic: "froling/S3Turbo/AIR_SECONDAIRE/state" + value_template: "{{ value_json.air_secondaire_sensor }}" + input_type: "input_register" + address: 30007 + offset: 30001 + precision: 0 + value_map: null + signed: false + - name: "Vitesse ventilateur" + unique_id: "vitesse_ventilateur_sensor" + type: "sensor" + # device_class: "None" + state_class: "measurement" + icon: "mdi:air-conditioner" + unit_of_measurement: "RPM" + state_topic: "froling/S3Turbo/VITESSE_VENTILATEUR/state" + value_template: "{{ value_json.vitesse_ventilateur_sensor }}" + input_type: "input_register" + address: 30008 + offset: 30001 + precision: 0 + value_map: null + signed: false + - name: "Commande tirage" + unique_id: "commande_tirage_sensor" + type: "sensor" + # device_class: "None" + state_class: "measurement" + icon: "mdi:percent-box" + unit_of_measurement: "%" + state_topic: "froling/S3Turbo/COMMANDE_TIRAGE/state" + value_template: "{{ value_json.commande_tirage_sensor }}" + input_type: "input_register" + address: 30016 + offset: 30001 + precision: 0 + value_map: null + signed: false + - name: "Consigne T° fumée" + unique_id: "consigne_temperature_fumee_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:temperature-celsius" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/CONSIGNE_TEMPERATURE_FUMEE/state" + value_template: "{{ value_json.consigne_temperature_fumee_sensor }}" + input_type: "input_register" + address: 30020 + offset: 30001 + scale: 1 + precision: 0 + value_map: null + signed: false + - name: "Consigne T° chauffage" + unique_id: "consigne_temperature_chauffage_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:temperature-celsius" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/CONSIGNE_TEMPERATURE_CHAUFFAGE/state" + value_template: "{{ value_json.consigne_temperature_chauffage_sensor }}" + input_type: "input_register" + address: 30023 + offset: 30001 + scale: 0.5 + precision: 0 + value_map: null + signed: false + - name: "Heure fonctionnement" + unique_id: "heure_fonctionnement_sensor" + type: "sensor" + device_class: "duration" + state_class: "total_increasing" + icon: "mdi:clock-time-eight-outline" + unit_of_measurement: "h" + state_topic: "froling/S3Turbo/HEURE FONCTIONNEMENT/state" + value_template: "{{ value_json.heure_fonctionnement_sensor }}" + input_type: "input_register" + address: 30099 + offset: 30001 + precision: 0 + value_map: null + signed: false + - name: "Heure de maintien de feu" + unique_id: "heure_maintien_de_feu_sensor" + type: "sensor" + device_class: "duration" + state_class: "total_increasing" + icon: "mdi:clock-time-eight-outline" + unit_of_measurement: "h" + state_topic: "froling/S3Turbo/HEURE MAINTIEN DE FEU/state" + value_template: "{{ value_json.heure_maintien_de_feu_sensor }}" + input_type: "input_register" + address: 30116 + offset: 30001 + precision: 0 + value_map: null + signed: false + - name: "Heure chauffage" + unique_id: "heure_chauffage_sensor" + type: "sensor" + device_class: "duration" + state_class: "total_increasing" + icon: "mdi:clock-time-eight-outline" + unit_of_measurement: "h" + state_topic: "froling/S3Turbo/HEURE CHAUFFAGE/state" + value_template: "{{ value_json.heure_chauffage_sensor }}" + input_type: "input_register" + address: 30222 + offset: 30001 + precision: 0 + value_map: null + signed: false + - name: "Tampon Haut" + unique_id: "tampon_haut_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:water-boiler" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/TAMPON_HAUT/state" + value_template: "{{ value_json.tampon_haut_sensor }}" + input_type: "input_register" + address: 30119 + offset: 30001 + scale: 0.5 + precision: 0 + value_map: null + signed: false + - name: "Tampon Bas" + unique_id: "tampon_bas_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:water-boiler" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/TAMPON_BAS/state" + value_template: "{{ value_json.tampon_bas_sensor }}" + input_type: "input_register" + address: 30121 + offset: 30001 + scale: 0.5 + precision: 0 + value_map: null + signed: false + - name: "T° depart chauffage" + unique_id: "temperature_depart_chauffage_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:thermometer" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/TEMPERATURE_DEPART_CHAUFFAGE/state" + value_template: "{{ value_json.temperature_depart_chauffage_sensor }}" + input_type: "input_register" + address: 30022 + offset: 30001 + scale: 0.5 + precision: 0 + value_map: null + signed: false + - name: "Charge tampon" + unique_id: "charge_tampon_sensor" + type: "sensor" + device_class: "battery" + state_class: "measurement" + icon: "mdi:percent-circle-outline" + unit_of_measurement: "%" + state_topic: "froling/S3Turbo/CHARGE TAMPON/state" + value_template: "{{ value_json.charge_tampon_sensor }}" + input_type: "input_register" + address: 30226 + offset: 30001 + precision: 0 + value_map: null + signed: false +# Digital Outputs +# Function: Read Coil Status (FC=01) + - name: pompe circuit chauffage" + unique_id: "pompe_circuit_chauffage" + type: "binary_sensor" + device_class: "running" + icon: "mdi:pump" + state_topic: "froling/S3Turbo/pump_chauffage/state" + value_template: "{{ value_json.charge_tampon_sensor }}" + input_type: "coil" + address: 30226 + offset: 30001 + precision: 0 + value_map: null + signed: false +# Digital Outputs +# Function: Read Coil Status (FC=01) + - name: pompe circuit_chauffage + unique_id: "pompe_circuit_chauffage" + type: "binary_sensor" + icon: "mdi:pump" + state_topic: "froling/S3Turbo/pump_chauffage/state" + value_template: "{{ value }}" + input_type: "coil" + address: 0 + offset: 0 + precision: 0 + value_map: null + signed: false + - name: cc_melangeur_ouvert + unique_id: "cc_melangeur_ouvert" + type: "binary_sensor" + icon: "mdi:pipe-valve" + state_topic: "froling/S3Turbo/cc_melangeur_ouvert/state" + value_template: "{{ value }}" + input_type: "coil" + address: 2 + offset: 0 + precision: 0 + value_map: null + signed: false + - name: cc_melangeur_ferme + unique_id: "cc_melangeur_ferme" + type: "binary_sensor" + icon: "mdi:pipe-valve" + state_topic: "froling/S3Turbo/cc_melangeur_ferme/state" + value_template: "{{ value }}" + input_type: "coil" + address: 3 + offset: 0 + precision: 0 + value_map: null + signed: false + diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100755 index 0000000..c89beb9 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,10 @@ +version: "3.8" + +services: + app: + container_name: froling_modbus2mqtt + build: . + volumes: + - ./src:/usr/src/app + - ./config:/usr/src/app/config + restart: unless-stopped diff --git a/exclude.txt b/exclude.txt new file mode 100755 index 0000000..1a43850 --- /dev/null +++ b/exclude.txt @@ -0,0 +1,3 @@ +modbus2mqtt/env/ +modbus2mqttsrc/__pycache__/ +modbus2mqtt/*.log \ No newline at end of file diff --git a/readme.txt b/readme.txt new file mode 100755 index 0000000..dcd8000 --- /dev/null +++ b/readme.txt @@ -0,0 +1,2 @@ +pour creer l'archive du dossier, se mettre dans le dossier parent et lancer la commande suivante : +tar -czvf froling_modbus2mqtt.tar.gz --exclude-from='modbus2mqtt/exclude.txt' -C modbus2mqtt . \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100755 index 0000000..71ac13a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +paho-mqtt==1.6.1 +pymodbus==3.5.4 +pyModbusTCP==0.2.0 +PyYAML==6.0.1 diff --git a/src/config.yaml b/src/config.yaml new file mode 100755 index 0000000..1aa4ff8 --- /dev/null +++ b/src/config.yaml @@ -0,0 +1,435 @@ +mqtt: + host: "10.0.0.3" + port: 1883 + user: "" + password: "" + topic_prefix: "froling/S3Turbo" + +homeassistant: + autodiscovery: True + discovery_prefix: "homeassistant" # Préfixe par défaut pour l'autodiscovery + node_id: "froling" # Remplacer par l'identifiant unique du dispositif + +modbus: + host: "10.0.0.12" + port: 502 + unit_id: 2 + timeout: 30 + +refresh_rate: 10 # Fréquence d'actualisation en secondes + +device: + identifiers: "FrolingS3" # Remplacer par l'identifiant unique du dispositif + manufacturer: "Froling" # Remplacer par le fabricant du dispositif + model: "S3" # Remplacer par le modèle du dispositif + name: "Froling S3" # Remplacer par le nom du dispositif + sw_version: "1.0" # Remplacer par la version du logiciel du dispositif, si applicable + # configuration_url: "http://example.com" # Remplacer par l'URL de configuration ou de documentation du dispositif + + +# Ajout des paramètres pour l'état de l'installation et l'état de la chaudière +value_maps: + SystemStatus: + 0: "Charge continue" + 1: "Eau chaude sanitaire" + 2: "Automatique" + 3: "Fonctionnement au bois de chauffage" + 4: "Nettoyage" + 5: "Éteint" + 6: "Chauffage supplémentaire" + 7: "Ramoneur" + 8: "Nettoyage2" + FurnaceStatus: + 0: "DÉFAUT" + 1: "Chaudière éteinte" + 2: "Montée en température" + 3: "Chauffage" + 4: "Maintien du feu" + 5: "Feu éteint" + 6: "Porte ouverte" + 7: "Préparation" + 8: "Préchauffage" + 9: "Allumage" + 10: "Attente d'arrêt" + 11: "Attente d'arrêt1" + 12: "Arrêt_Alimentation1" + 13: "Arrêt_Attente2" + 14: "Arrêt_Alimentation2" + 15: "Nettoyage" + 16: "Attente_2h" + 17: "Aspiration_Chauffage" + 18: "Défaut_d'allumage" + 19: "Prêt_à_l'emploi" + +entities: + - name: "Etats systeme" + unique_id: "etats_systeme_sensor" + type: "sensor" + # device_class: "enum" + # state_class: "measurement" + icon: "mdi:radiator" + unit_of_measurement: "" + state_topic: "froling/S3Turbo/ETATS_SYSTEME/state" + value_template: "{{ value_json.etats_systeme_sensor }}" + input_type: "input_register" + address: 34001 + offset: 30001 + scale: 1 + precision: 1 + value_map: "SystemStatus" + signed: false + - name: "Etat Chaudiere" + unique_id: "etats_chaudiere_sensor" + type: "sensor" + # device_class: "enum" + # state_class: "measurement" + icon: "mdi:water-boiler-alert" + unit_of_measurement: "" + state_topic: "froling/S3Turbo/ETATS_CHAUDIERE/state" + value_template: "{{ value_json.etats_chaudiere_sensor }}" + input_type: "input_register" + address: 34002 + offset: 30001 + scale: 1 + precision: 0 + value_map: "FurnaceStatus" + signed: false + - name: "T° Chaudiere" + unique_id: "temperature_chaudiere_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:hydraulic-oil-temperature" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/TEMPERATURE_CHAUDIERE/state" + value_template: "{{ value_json.temperature_chaudiere_sensor }}" + input_type: "input_register" + address: 30001 + offset: 30001 + scale: 0.5 + precision: 0 + value_map: null + signed: false + - name: "T° Fumee" + unique_id: "temperature_fumee_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:smoke" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/TEMPERATURE_FUMEE/state" + value_template: "{{ value_json.temperature_fumee_sensor }}" + input_type: "input_register" + address: 30002 + offset: 30001 + scale: 1 + precision: 0 + value_map: null + signed: false + - name: "T° Board" + unique_id: "temperature_board_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:thermometer-lines" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/TEMPERATURE_BOARD/state" + value_template: "{{ value_json.temperature_board_sensor }}" + input_type: "input_register" + address: 30003 + offset: 30001 + scale: 0.5 + precision: 0 + value_map: null + signed: false + - name: "O2 residuel" + unique_id: "o2_residuel_sensor" + type: "sensor" + device_class: "battery" + state_class: "measurement" + icon: "mdi:percent" + unit_of_measurement: "%" + state_topic: "froling/S3Turbo/O2_RESIDUEL/state" + value_template: "{{ value_json.o2_residuel_sensor }}" + input_type: "input_register" + address: 30004 + offset: 30001 + scale: 0.1 + precision: 0 + value_map: null + signed: false + - name: "T° Exterieur" + unique_id: "temp_exterieur_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:home-thermometer-outline" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/TEMP_EXTERIEUR/state" + value_template: "{{ value_json.temp_exterieur_sensor }}" + input_type: "input_register" + address: 30005 + offset: 30001 + scale: 0.5 + precision: 0 + value_map: null + signed: true + - name: "Porte chaudiere" + unique_id: "porte_chaudiere_sensor" + type: "binary_sensor" + device_class: "door" + payload_on: "1" + payload_off: "0" + #state_class: "measurement" + icon: "mdi:door-open" + #unit_of_measurement: "" + state_topic: "froling/S3Turbo/PORTE_CHAUDIERE/state" + value_template: "{{ value }}" + input_type: "input_status" + address: 10001 + offset: 10001 + value_map: null + signed: false + + + - name: "Air primaire" + unique_id: "air_primaire_sensor" + type: "sensor" + device_class: "battery" + state_class: "measurement" + icon: "mdi:air-filter" + unit_of_measurement: "%" + state_topic: "froling/S3Turbo/AIR_PRIMAIRE/state" + value_template: "{{ value_json.air_primaire_sensor }}" + input_type: "input_register" + address: 30006 + offset: 30001 + precision: 0 + value_map: null + signed: false + - name: "Air Secondaire" + unique_id: "air_secondaire_sensor" + type: "sensor" + device_class: "Battery" + state_class: "measurement" + icon: "mdi:air-filter" + unit_of_measurement: "%" + state_topic: "froling/S3Turbo/AIR_SECONDAIRE/state" + value_template: "{{ value_json.air_secondaire_sensor }}" + input_type: "input_register" + address: 30007 + offset: 30001 + precision: 0 + value_map: null + signed: false + - name: "Vitesse ventilateur" + unique_id: "vitesse_ventilateur_sensor" + type: "sensor" + # device_class: "None" + state_class: "measurement" + icon: "mdi:air-conditioner" + unit_of_measurement: "RPM" + state_topic: "froling/S3Turbo/VITESSE_VENTILATEUR/state" + value_template: "{{ value_json.vitesse_ventilateur_sensor }}" + input_type: "input_register" + address: 30008 + offset: 30001 + precision: 0 + value_map: null + signed: false + - name: "Commande tirage" + unique_id: "commande_tirage_sensor" + type: "sensor" + # device_class: "None" + state_class: "measurement" + icon: "mdi:percent-box" + unit_of_measurement: "%" + state_topic: "froling/S3Turbo/COMMANDE_TIRAGE/state" + value_template: "{{ value_json.commande_tirage_sensor }}" + input_type: "input_register" + address: 30016 + offset: 30001 + precision: 0 + value_map: null + signed: false + - name: "Consigne T° fumée" + unique_id: "consigne_temperature_fumee_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:temperature-celsius" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/CONSIGNE_TEMPERATURE_FUMEE/state" + value_template: "{{ value_json.consigne_temperature_fumee_sensor }}" + input_type: "input_register" + address: 30020 + offset: 30001 + scale: 1 + precision: 0 + value_map: null + signed: false + - name: "Consigne T° chauffage" + unique_id: "consigne_temperature_chauffage_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:temperature-celsius" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/CONSIGNE_TEMPERATURE_CHAUFFAGE/state" + value_template: "{{ value_json.consigne_temperature_chauffage_sensor }}" + input_type: "input_register" + address: 30023 + offset: 30001 + scale: 0.5 + precision: 0 + value_map: null + signed: false + - name: "Heure fonctionnement" + unique_id: "heure_fonctionnement_sensor" + type: "sensor" + device_class: "duration" + state_class: "total_increasing" + icon: "mdi:clock-time-eight-outline" + unit_of_measurement: "h" + state_topic: "froling/S3Turbo/HEURE FONCTIONNEMENT/state" + value_template: "{{ value_json.heure_fonctionnement_sensor }}" + input_type: "input_register" + address: 30099 + offset: 30001 + precision: 0 + value_map: null + signed: false + - name: "Heure de maintien de feu" + unique_id: "heure_maintien_de_feu_sensor" + type: "sensor" + device_class: "duration" + state_class: "total_increasing" + icon: "mdi:clock-time-eight-outline" + unit_of_measurement: "h" + state_topic: "froling/S3Turbo/HEURE MAINTIEN DE FEU/state" + value_template: "{{ value_json.heure_maintien_de_feu_sensor }}" + input_type: "input_register" + address: 30116 + offset: 30001 + precision: 0 + value_map: null + signed: false + - name: "Heure chauffage" + unique_id: "heure_chauffage_sensor" + type: "sensor" + device_class: "duration" + state_class: "total_increasing" + icon: "mdi:clock-time-eight-outline" + unit_of_measurement: "h" + state_topic: "froling/S3Turbo/HEURE CHAUFFAGE/state" + value_template: "{{ value_json.heure_chauffage_sensor }}" + input_type: "input_register" + address: 30222 + offset: 30001 + precision: 0 + value_map: null + signed: false + - name: "Tampon Haut" + unique_id: "tampon_haut_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:water-boiler" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/TAMPON_HAUT/state" + value_template: "{{ value_json.tampon_haut_sensor }}" + input_type: "input_register" + address: 30119 + offset: 30001 + scale: 0.5 + precision: 0 + value_map: null + signed: false + - name: "Tampon Bas" + unique_id: "tampon_bas_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:water-boiler" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/TAMPON_BAS/state" + value_template: "{{ value_json.tampon_bas_sensor }}" + input_type: "input_register" + address: 30121 + offset: 30001 + scale: 0.5 + precision: 0 + value_map: null + signed: false + - name: "T° depart chauffage" + unique_id: "temperature_depart_chauffage_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:thermometer" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/TEMPERATURE_DEPART_CHAUFFAGE/state" + value_template: "{{ value_json.temperature_depart_chauffage_sensor }}" + input_type: "input_register" + address: 30022 + offset: 30001 + scale: 0.5 + precision: 0 + value_map: null + signed: false + - name: "Charge tampon" + unique_id: "charge_tampon_sensor" + type: "sensor" + device_class: "battery" + state_class: "measurement" + icon: "mdi:percent-circle-outline" + unit_of_measurement: "%" + state_topic: "froling/S3Turbo/CHARGE TAMPON/state" + value_template: "{{ value_json.charge_tampon_sensor }}" + input_type: "input_register" + address: 30226 + offset: 30001 + precision: 0 + value_map: null + signed: false +# Digital Outputs +# Function: Read Coil Status (FC=01) + - name: pompe circuit_chauffage + unique_id: "pompe_circuit_chauffage" + type: "binary_sensor" + icon: "mdi:pump" + state_topic: "froling/S3Turbo/pump_chauffage/state" + value_template: "{{ value }}" + input_type: "coil" + address: 0 + offset: 0 + precision: 0 + value_map: null + signed: false + - name: cc_melangeur_ouvert + unique_id: "cc_melangeur_ouvert" + type: "binary_sensor" + icon: "mdi:pipe-valve" + state_topic: "froling/S3Turbo/cc_melangeur_ouvert/state" + value_template: "{{ value }}" + input_type: "coil" + address: 2 + offset: 0 + precision: 0 + value_map: null + signed: false + - name: cc_melangeur_ferme + unique_id: "cc_melangeur_ferme" + type: "binary_sensor" + icon: "mdi:pipe-valve" + state_topic: "froling/S3Turbo/cc_melangeur_ferme/state" + value_template: "{{ value }}" + input_type: "coil" + address: 3 + offset: 0 + precision: 0 + value_map: null + signed: false + + + diff --git a/src/config/config.yaml b/src/config/config.yaml new file mode 100755 index 0000000..b11f043 --- /dev/null +++ b/src/config/config.yaml @@ -0,0 +1,435 @@ +mqtt: + host: "10.0.0.3" + port: 1883 + user: "" + password: "" + topic_prefix: "froling/S3Turbo" + +homeassistant: + autodiscovery: True + discovery_prefix: "homeassistant" # Préfixe par défaut pour l'autodiscovery + node_id: "froling" # Remplacer par l'identifiant unique du dispositif + +modbus: + host: "10.0.0.12" + port: 502 + unit_id: 2 + timeout: 30 + +refresh_rate: 10 # Fréquence d'actualisation en secondes + +device: + identifiers: "FrolingS3" # Remplacer par l'identifiant unique du dispositif + manufacturer: "Froling" # Remplacer par le fabricant du dispositif + model: "S3" # Remplacer par le modèle du dispositif + name: "Froling S3" # Remplacer par le nom du dispositif + sw_version: "1.0" # Remplacer par la version du logiciel du dispositif, si applicable + # configuration_url: "http://example.com" # Remplacer par l'URL de configuration ou de documentation du dispositif + + +# Ajout des paramètres pour l'état de l'installation et l'état de la chaudière +value_maps: + SystemStatus: + 0: "Charge continue" + 1: "Eau chaude sanitaire" + 2: "Automatique" + 3: "Fonctionnement au bois de chauffage" + 4: "Nettoyage" + 5: "Éteint" + 6: "Chauffage supplémentaire" + 7: "Ramoneur" + 8: "Nettoyage2" + FurnaceStatus: + 0: "DÉFAUT" + 1: "Chaudière éteinte" + 2: "Montée en température" + 3: "Chauffage" + 4: "Maintien du feu" + 5: "Feu éteint" + 6: "Porte ouverte" + 7: "Préparation" + 8: "Préchauffage" + 9: "Allumage" + 10: "Attente d'arrêt" + 11: "Attente d'arrêt1" + 12: "Arrêt_Alimentation1" + 13: "Arrêt_Attente2" + 14: "Arrêt_Alimentation2" + 15: "Nettoyage" + 16: "Attente_2h" + 17: "Aspiration_Chauffage" + 18: "Défaut_d'allumage" + 19: "Prêt_à_l'emploi" + +entities: + - name: "Etats systeme" + unique_id: "etats_systeme_sensor" + type: "sensor" + # device_class: "enum" + # state_class: "measurement" + icon: "mdi:radiator" + unit_of_measurement: "" + state_topic: "froling/S3Turbo/ETATS_SYSTEME/state" + value_template: "{{ value_json.etats_systeme_sensor }}" + input_type: "input_register" + address: 34001 + offset: 30001 + scale: 1 + precision: 1 + value_map: "SystemStatus" + signed: false + - name: "Etat Chaudiere" + unique_id: "etats_chaudiere_sensor" + type: "sensor" + # device_class: "enum" + # state_class: "measurement" + icon: "mdi:water-boiler-alert" + unit_of_measurement: "" + state_topic: "froling/S3Turbo/ETATS_CHAUDIERE/state" + value_template: "{{ value_json.etats_chaudiere_sensor }}" + input_type: "input_register" + address: 34002 + offset: 30001 + scale: 1 + precision: 0 + value_map: "FurnaceStatus" + signed: false + - name: "T° Chaudiere" + unique_id: "temperature_chaudiere_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:hydraulic-oil-temperature" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/TEMPERATURE_CHAUDIERE/state" + value_template: "{{ value_json.temperature_chaudiere_sensor }}" + input_type: "input_register" + address: 30001 + offset: 30001 + scale: 0.5 + precision: 0 + value_map: null + signed: false + - name: "T° Fumee" + unique_id: "temperature_fumee_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:smoke" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/TEMPERATURE_FUMEE/state" + value_template: "{{ value_json.temperature_fumee_sensor }}" + input_type: "input_register" + address: 30002 + offset: 30001 + scale: 1 + precision: 0 + value_map: null + signed: false + - name: "T° Board" + unique_id: "temperature_board_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:thermometer-lines" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/TEMPERATURE_BOARD/state" + value_template: "{{ value_json.temperature_board_sensor }}" + input_type: "input_register" + address: 30003 + offset: 30001 + scale: 0.5 + precision: 0 + value_map: null + signed: false + - name: "O2 residuel" + unique_id: "o2_residuel_sensor" + type: "sensor" + device_class: "battery" + state_class: "measurement" + icon: "mdi:percent" + unit_of_measurement: "%" + state_topic: "froling/S3Turbo/O2_RESIDUEL/state" + value_template: "{{ value_json.o2_residuel_sensor }}" + input_type: "input_register" + address: 30004 + offset: 30001 + scale: 0.1 + precision: 0 + value_map: null + signed: false + - name: "T° Exterieur" + unique_id: "temp_exterieur_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:home-thermometer-outline" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/TEMP_EXTERIEUR/state" + value_template: "{{ value_json.temp_exterieur_sensor }}" + input_type: "input_register" + address: 30005 + offset: 30001 + scale: 0.5 + precision: 0 + value_map: null + signed: true + - name: "Porte chaudiere" + unique_id: "porte_chaudiere_sensor" + type: "binary_sensor" + device_class: "door" + payload_on: "1" + payload_off: "0" + #state_class: "measurement" + icon: "mdi:door-open" + #unit_of_measurement: "" + state_topic: "froling/S3Turbo/PORTE_CHAUDIERE/state" + value_template: "{{ value }}" + input_type: "input_status" + address: 10001 + offset: 10001 + value_map: null + signed: false + + + - name: "Air primaire" + unique_id: "air_primaire_sensor" + type: "sensor" + device_class: "battery" + state_class: "measurement" + icon: "mdi:air-filter" + unit_of_measurement: "%" + state_topic: "froling/S3Turbo/AIR_PRIMAIRE/state" + value_template: "{{ value_json.air_primaire_sensor }}" + input_type: "input_register" + address: 30006 + offset: 30001 + precision: 0 + value_map: null + signed: false + - name: "Air Secondaire" + unique_id: "air_secondaire_sensor" + type: "sensor" + device_class: "Battery" + state_class: "measurement" + icon: "mdi:air-filter" + unit_of_measurement: "%" + state_topic: "froling/S3Turbo/AIR_SECONDAIRE/state" + value_template: "{{ value_json.air_secondaire_sensor }}" + input_type: "input_register" + address: 30007 + offset: 30001 + precision: 0 + value_map: null + signed: false + - name: "Vitesse ventilateur" + unique_id: "vitesse_ventilateur_sensor" + type: "sensor" + # device_class: "None" + state_class: "measurement" + icon: "mdi:air-conditioner" + unit_of_measurement: "RPM" + state_topic: "froling/S3Turbo/VITESSE_VENTILATEUR/state" + value_template: "{{ value_json.vitesse_ventilateur_sensor }}" + input_type: "input_register" + address: 30008 + offset: 30001 + precision: 0 + value_map: null + signed: false + - name: "Commande tirage" + unique_id: "commande_tirage_sensor" + type: "sensor" + # device_class: "None" + state_class: "measurement" + icon: "mdi:percent-box" + unit_of_measurement: "%" + state_topic: "froling/S3Turbo/COMMANDE_TIRAGE/state" + value_template: "{{ value_json.commande_tirage_sensor }}" + input_type: "input_register" + address: 30016 + offset: 30001 + precision: 0 + value_map: null + signed: false + - name: "Consigne T° fumée" + unique_id: "consigne_temperature_fumee_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:temperature-celsius" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/CONSIGNE_TEMPERATURE_FUMEE/state" + value_template: "{{ value_json.consigne_temperature_fumee_sensor }}" + input_type: "input_register" + address: 30020 + offset: 30001 + scale: 1 + precision: 0 + value_map: null + signed: false + - name: "Consigne T° chauffage" + unique_id: "consigne_temperature_chauffage_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:temperature-celsius" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/CONSIGNE_TEMPERATURE_CHAUFFAGE/state" + value_template: "{{ value_json.consigne_temperature_chauffage_sensor }}" + input_type: "input_register" + address: 30023 + offset: 30001 + scale: 0.5 + precision: 0 + value_map: null + signed: false + - name: "Heure fonctionnement" + unique_id: "heure_fonctionnement_sensor" + type: "sensor" + device_class: "duration" + state_class: "total_increasing" + icon: "mdi:clock-time-eight-outline" + unit_of_measurement: "h" + state_topic: "froling/S3Turbo/HEURE FONCTIONNEMENT/state" + value_template: "{{ value_json.heure_fonctionnement_sensor }}" + input_type: "input_register" + address: 30099 + offset: 30001 + precision: 0 + value_map: null + signed: false + - name: "Heure de maintien de feu" + unique_id: "heure_maintien_de_feu_sensor" + type: "sensor" + device_class: "duration" + state_class: "total_increasing" + icon: "mdi:clock-time-eight-outline" + unit_of_measurement: "h" + state_topic: "froling/S3Turbo/HEURE MAINTIEN DE FEU/state" + value_template: "{{ value_json.heure_maintien_de_feu_sensor }}" + input_type: "input_register" + address: 30116 + offset: 30001 + precision: 0 + value_map: null + signed: false + - name: "Heure chauffage" + unique_id: "heure_chauffage_sensor" + type: "sensor" + device_class: "duration" + state_class: "total_increasing" + icon: "mdi:clock-time-eight-outline" + unit_of_measurement: "h" + state_topic: "froling/S3Turbo/HEURE CHAUFFAGE/state" + value_template: "{{ value_json.heure_chauffage_sensor }}" + input_type: "input_register" + address: 30222 + offset: 30001 + precision: 0 + value_map: null + signed: false + - name: "Tampon Haut" + unique_id: "tampon_haut_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:water-boiler" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/TAMPON_HAUT/state" + value_template: "{{ value_json.tampon_haut_sensor }}" + input_type: "input_register" + address: 30119 + offset: 30001 + scale: 0.5 + precision: 0 + value_map: null + signed: false + - name: "Tampon Bas" + unique_id: "tampon_bas_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:water-boiler" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/TAMPON_BAS/state" + value_template: "{{ value_json.tampon_bas_sensor }}" + input_type: "input_register" + address: 30121 + offset: 30001 + scale: 0.5 + precision: 0 + value_map: null + signed: false + - name: "T° depart chauffage" + unique_id: "temperature_depart_chauffage_sensor" + type: "sensor" + device_class: "temperature" + state_class: "measurement" + icon: "mdi:thermometer" + unit_of_measurement: "°C" + state_topic: "froling/S3Turbo/TEMPERATURE_DEPART_CHAUFFAGE/state" + value_template: "{{ value_json.temperature_depart_chauffage_sensor }}" + input_type: "input_register" + address: 30022 + offset: 30001 + scale: 0.5 + precision: 0 + value_map: null + signed: false + - name: "Charge tampon" + unique_id: "charge_tampon_sensor" + type: "sensor" + device_class: "battery" + state_class: "measurement" + icon: "mdi:percent-circle-outline" + unit_of_measurement: "%" + state_topic: "froling/S3Turbo/CHARGE TAMPON/state" + value_template: "{{ value_json.charge_tampon_sensor }}" + input_type: "input_register" + address: 30226 + offset: 30001 + precision: 0 + value_map: null + signed: false +# Digital Outputs +# Function: Read Coil Status (FC=01) + - name: pompe circuit_chauffage + unique_id: "pompe_circuit_chauffage" + type: "binary_sensor" + icon: "mdi:pump" + state_topic: "froling/S3Turbo/pump_chauffage/state" + value_template: "{{ value }}" + input_type: "coil" + address: 0 + offset: 0 + precision: 0 + value_map: null + signed: false + - name: cc_melangeur_ouvert + unique_id: "cc_melangeur_ouvert" + type: "binary_sensor" + icon: "mdi:pipe-valve" + state_topic: "froling/S3Turbo/cc_melangeur_ouvert/state" + value_template: "{{ value }}" + input_type: "coil" + address: 2 + offset: 0 + precision: 0 + value_map: null + signed: false + - name: cc_melangeur_ferme + unique_id: "cc_melangeur_ferme" + type: "binary_sensor" + icon: "mdi:pipe-valve" + state_topic: "froling/S3Turbo/cc_melangeur_ferme/state" + value_template: "{{ value }}" + input_type: "coil" + address: 3 + offset: 0 + precision: 0 + value_map: null + signed: false + + + diff --git a/src/main.py b/src/main.py new file mode 100755 index 0000000..2114ca5 --- /dev/null +++ b/src/main.py @@ -0,0 +1,278 @@ +import os +import time +import yaml +import json +import paho.mqtt.client as mqtt +from pyModbusTCP.client import ModbusClient + + +""" +Types de registres Modbus et fonctions associées : + +1. Coils (Bobines) : sortie digital - light - relay + - Fonction Modbus : 01 (FC01) + - Lecture : read_coils() + - Écriture : write_single_coil() ou write_multiple_coils() + - Plage d'adresses typique : 00001-09999 + +2. Discrete Inputs (Entrées Discrètes) : entrée digital - binary_sensor - switch + - Fonction Modbus : 02 (FC02) + - Lecture : read_discrete_inputs() + - Écriture : Non applicable (lecture seule) + - Plage d'adresses typique : 10001-19999 + +3. Input Registers (Registres d'Entrée) : valeur actuelle - sensor + - Fonction Modbus : 04 (FC04) + - Lecture : read_input_registers() + - Écriture : Non applicable (lecture seule) + - Plage d'adresses typique : 30001-39999 + +4. Holding Registers (Registres de Maintien) : parameters + - Fonction Modbus : 03 (FC03) + - Lecture : read_holding_registers() + - Écriture : write_single_register() ou write_multiple_registers() + - Plage d'adresses typique : 40001-49999 + +Note : Les adresses Modbus sont souvent spécifiées avec leur 'offset' - c'est-à-dire que l'adresse réelle utilisée dans le protocole est l'adresse spécifiée moins un. Par exemple, l'adresse 40001 serait l'adresse 0 dans le message Modbus. +""" + + +def load_config(): + # Obtenez le chemin du dossier actuel où se trouve main.py + current_dir = os.path.dirname(os.path.abspath(__file__)) + # Construisez le chemin vers config.yaml + config_path = os.path.join(current_dir, 'config.yaml') # Suppression du sous-dossier 'config' + with open(config_path, 'r') as file: + config = yaml.safe_load(file) + return config + +# Ajout de la fonction de publication des messages de découverte +def publish_discovery_messages(client, config): + base_topic = config['homeassistant']['discovery_prefix'] + node_id = config['homeassistant']['node_id'] + device_info = config['device'] + + for entity in config['entities']: + component = entity['type'] # 'sensor', 'binary_sensor', etc. + object_id = entity['unique_id'] + discovery_topic = f"{base_topic}/{component}/{node_id}/{object_id}/config" + message = { + "availability_topic": f"{config['mqtt']['topic_prefix']}/availability", + "icon": entity.get('icon', ''), + "unique_id": entity['unique_id'], + "device": device_info, + "name": entity['name'], + "state_topic": f"{config['mqtt']['topic_prefix']}/{entity['name']}/state", + #"value_template": "{{ value }}", # Ajouter une value_template si nécessaire + #"device_class": entity.get('device_class', ''), + #"unit_of_measurement": entity.get('unit_of_measurement', '')# La valeur par défaut est '°C + + + } + if "unit_of_measurement" in entity: message["unit_of_measurement"] = entity["unit_of_measurement"] + if "state_class" in entity: message["state_class"] = entity["state_class"] + if "device_class" in entity: message["device_class"] = entity["device_class"] + + if component == "binary_sensor": + message.update({ + "payload_on": entity.get('payload_on', '1'), # La valeur par défaut est 'ON' + "payload_off": entity.get('payload_off', '0'), # La valeur par défaut est 'OFF' + "value_template": entity.get('value_template', "{{ value_json.value }}"), # Ajouter une value_template si nécessaire + }) + client.publish(discovery_topic, json.dumps(message), qos=1, retain=True) + +def publish_availability(client, status, config): + availability_topic = f"{config['mqtt']['topic_prefix']}/availability" + client.publish(availability_topic, status, qos=1, retain=True) + +def convert_to_signed(value): + NEGATIVE_THRESHOLD = 32000 # Seuil pour déterminer les valeurs négatives + MAX_VALUE = 32767 + + if value >= NEGATIVE_THRESHOLD: + # Interpréter comme une température négative + return -(MAX_VALUE - value + 1) + return value + +def on_connect(client, userdata, flags, rc): + if rc == 0: + print("Connected to MQTT Broker!") + publish_availability(client, 'online', userdata) + # Si l'autodiscovery est activé, publiez les messages de découverte + if userdata['homeassistant']['autodiscovery']: + publish_discovery_messages(client, userdata) + else: + print("Failed to connect, return code %d\n", rc) + +def on_publish(client, userdata, result): + print(f"Data published to MQTT. Result: {result}") + + +def connect_modbus(host, port, unit_id, timeout): + """ + Connect to the Modbus server. + """ + client = ModbusClient() + client.host = host # Set the host + client.port = port # Set the port + client.unit_id = unit_id + client.timeout = timeout + connection = client.open() + + if connection: + print(f"Connected to Modbus server at {host}:{port}") + else: + print(f"Failed to connect to Modbus server at {host}:{port}") + + return client + +def read_modbus_registers(client, input_type, address, offset, scale=1, precision=0, count=1): + """ + Read registers from the Modbus server. + + :param client: the Modbus client instance + :param input_type: the type of the register (input_register, holding_register) + :param address: the address of the register to read + :param offset: the offset to apply to the address + :param scale: the scaling factor to apply to the read values + :param precision: the number of decimal places to round the scaled values + :param count: the number of registers to read + :param value_map: a dictionary mapping register values to human-readable strings + :return: the scaled and rounded value of the registers or None if an error occurs + """ + print(f"Address from call: {address}, Offset from call: {offset}") + # Calculate the real address by subtracting the offset + real_address = address - offset + print(f"Preparing to read Modbus registers with initial address: {address}, " + f"offset: {offset}, resulting in real address: {real_address}") + try: + regs = None + if input_type == 'input_register': + # Use FC04 to read input registers + print(f"Reading {count} input register(s) starting at initial address {address} " + f"(real address {real_address}, offset {offset}) using FC04") + regs = client.read_input_registers(real_address, count) + elif input_type == 'holding_register': + # Use FC03 to read holding registers + print(f"Reading {count} holding register(s) starting at initial address {address} " + f"(real address {real_address}, offset {offset}) using FC03") + regs = client.read_holding_registers(real_address, count) + elif input_type == 'input_status': + # Use FC02 to read discrete inputs + print(f"Reading {count} input status(es) starting at initial address {address} " + f"(real address {real_address}, offset {offset}) using FC02") + regs = client.read_discrete_inputs(real_address, count) + elif input_type == 'coil': + # Use FC01 to read coils + print(f"Reading {count} coil(s) starting at initial address {address} " + f"(real address {real_address}, offset {offset}) using FC01") + regs = client.read_coils(real_address, count) + else: + print(f"Invalid input type: {input_type}") + return None + + if regs is not None: + + # Apply scaling and rounding to the read values + scaled_and_rounded_regs = [round(reg * scale, precision) for reg in regs] + print(f"Read successful: {scaled_and_rounded_regs}") + return scaled_and_rounded_regs + else: + print(f"Read error at initial address {address} (real address {real_address}, offset {offset})") + return None + except Exception as e: + print(f"Modbus read error at initial address {address} (real address {real_address}, offset {offset}): {e}") + return None + + + + +def main(): + config = load_config() + + refresh_rate = config['refresh_rate'] # Obtenez la fréquence d'actualisation depuis la configuration + + # Configuration MQTT + mqtt_config = config['mqtt'] + mqtt_client = mqtt.Client() + mqtt_client.user_data_set(config) # Passer la configuration comme userdata + mqtt_client.on_connect = on_connect + mqtt_client.on_publish = on_publish + if mqtt_config['user'] and mqtt_config['password']: + mqtt_client.username_pw_set(mqtt_config['user'], mqtt_config['password']) + mqtt_client.connect(mqtt_config['host'], mqtt_config['port'], 60) + mqtt_client.loop_start() + + while True: + modbus_client = None + try: + # etape de connexion Modbus + # Affichage de la configuration pour la vérification + + # print("MQTT Configuration:") + # print(config['mqtt']) + # print("\nModbus Configuration:") + # print(config['modbus']) + # print("\nHome Assistant Configuration:") + # print(config['homeassistant']) + # print("\nRefresh Rate:") + # print(config['refresh_rate']) + # print("\nDevice:") + # for device in config['device']: + # print(device) + # print("\nEntities:") + # for entity in config['entities']: + # print(entity) + + + # Étape de connexion Modbus + modbus_client = connect_modbus(config['modbus']['host'], + config['modbus']['port'], + config['modbus']['unit_id'], + config['modbus']['timeout']) + if modbus_client: + # Lecture des registres Modbus pour chaque entité + for entity in config['entities']: + input_type = entity['input_type'] + address = entity['address'] + offset = entity['offset'] # Utilisation de l'offset de l'entité + scale = entity.get('scale', 1) + precision = entity.get('precision', 0) # Utilisation de get pour une valeur par défaut si non présente + count = 1 # Supposons que count est toujours 1 pour simplifier, à ajuster si nécessaire + + # Affichage de l'entité et de l'offset avant la lecture + print(f"Reading entity: {entity['name']} with offset: {offset}") + # Lecture des registres avec l'offset correct + regs = read_modbus_registers(modbus_client, input_type, address, offset, scale, precision, count) + if regs is not None: + + value = regs[0] # Prend la première valeur si plusieurs registres sont lus + if entity.get('signed', False): + value = convert_to_signed(value) + print(f"Valeur convertie en signée: {value}") + + # Publier la valeur lue sur le topic MQTT approprié + topic = f"{config['mqtt']['topic_prefix']}/{entity['name']}/state" + mqtt_client.publish(topic, value) + print(f"Published value for {entity['name']}: {value}") + else: + print(f"Failed to read value for {entity['name']}") + + # Assurez-vous de fermer la connexion Modbus lorsque vous avez terminé + modbus_client.close() + else: + print("Failed to connect to the Modbus server.") + + except Exception as e: + print(f"An error occurred: {e}") + publish_availability(mqtt_client, 'offline', config) + finally: + if modbus_client: + modbus_client.close() + + # Attendez la fréquence d'actualisation avant de recommencer + print(f"Waiting for {refresh_rate} seconds before the next update.") + time.sleep(refresh_rate) + +if __name__ == '__main__': + main()