From 06f222fa4c217aa63bc1e5d1e13c3ee8ae36be05 Mon Sep 17 00:00:00 2001 From: Jesse Kleemann Date: Wed, 7 Aug 2024 01:14:14 +0200 Subject: [PATCH] v1.0.10-dev-async: use async, no sungrow encryption --- Dockerfile | 5 +---- modbus_handler.py | 21 ++++++++++----------- pypy.Dockerfile | 2 +- requirements.txt | 1 - sungrowmodbus2mqtt.py | 25 ++++++++++++++++--------- 5 files changed, 28 insertions(+), 26 deletions(-) diff --git a/Dockerfile b/Dockerfile index f774c53..8ab9756 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,9 +4,6 @@ WORKDIR /usr/src/app COPY . . -ENV PYTHONPATH=/usr/lib/python3.12/site-packages - -RUN apk --no-cache add py3-pycryptodomex && \ - pip install --no-cache-dir --prefer-binary -r requirements.txt +RUN pip install --no-cache-dir --prefer-binary -r requirements.txt CMD [ "python", "./sungrowmodbus2mqtt.py" ] diff --git a/modbus_handler.py b/modbus_handler.py index 224a61b..9f98abf 100644 --- a/modbus_handler.py +++ b/modbus_handler.py @@ -1,8 +1,8 @@ import logging -from time import sleep +from asyncio import sleep +from pymodbus.client import AsyncModbusTcpClient from pymodbus.exceptions import ConnectionException -from SungrowModbusTcpClient.SungrowModbusTcpClient import SungrowModbusTcpClient from config import config @@ -12,33 +12,32 @@ def __init__(self): self.host = config['ip'] self.port = config.get('port', 502) self.slave_id = config.get('slave_id', 0x1) - self.modbus_client = SungrowModbusTcpClient(host=self.host, port=self.port, timeout=10, retries=1) - self.reconnect(first_connect=True) + self.modbus_client = AsyncModbusTcpClient(host=self.host, port=self.port, timeout=10, retries=1) - def reconnect(self, first_connect=False): + async def reconnect(self, first_connect=False): while True: try: - connected = self.modbus_client.connect() + connected = await self.modbus_client.connect() except (ConnectionResetError, ConnectionException) as e: logging.error(f'modbus connect to {self.host}:{self.port} failed: {e}.') connected = False if connected: logging.info('modbus connected.' if first_connect else 'modbus reconnected.') break - sleep(1) + await sleep(1) - def read(self, table, address, count): + async def read(self, table, address, count): while True: try: if table == 'holding': - result = self.modbus_client.read_holding_registers(address, count, self.slave_id) + result = await self.modbus_client.read_holding_registers(address, count, self.slave_id) elif table == 'input': - result = self.modbus_client.read_input_registers(address, count, self.slave_id) + result = await self.modbus_client.read_input_registers(address, count, self.slave_id) else: raise Exception('Invalid table') except (ConnectionResetError, ConnectionException) as e: logging.error(f'modbus read failed: {e}.') - self.reconnect() + await self.reconnect() continue return result.registers diff --git a/pypy.Dockerfile b/pypy.Dockerfile index ebce2bf..9bf9621 100644 --- a/pypy.Dockerfile +++ b/pypy.Dockerfile @@ -6,7 +6,7 @@ COPY . . SHELL [ "/bin/bash", "-c" ] -RUN apt-get -y update && apt-get install -y build-essential pypy3 pypy3-venv && \ +RUN apt-get -y update && apt-get install -y pypy3 pypy3-venv && \ pypy3 -m venv venv && \ source venv/bin/activate && \ pypy3 -mpip install --no-cache-dir --prefer-binary -r requirements.txt && \ diff --git a/requirements.txt b/requirements.txt index c609f90..08b0412 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ pymodbus~=3.7.0 -SungrowModbusTcpClient-jesseklm~=0.1.9 paho-mqtt~=2.1.0 PyYAML~=6.0.1 diff --git a/sungrowmodbus2mqtt.py b/sungrowmodbus2mqtt.py index 06f4094..5cd2aef 100644 --- a/sungrowmodbus2mqtt.py +++ b/sungrowmodbus2mqtt.py @@ -1,13 +1,15 @@ +import asyncio import logging import signal import sys -from time import sleep, time +from asyncio import sleep +from time import time from config import config from modbus_handler import ModbusHandler from mqtt_handler import MqttHandler -__version__ = '1.0.9' +__version__ = '1.0.10-dev-async' def convert_to_type(value: int, datatype: str) -> int: @@ -38,15 +40,16 @@ def __init__(self): } self.init_registers() - def loop(self): + async def loop(self): + await self.modbus_handler.reconnect(first_connect=True) while True: start_time = time() - self.read() + await self.read() self.publish() time_taken = time() - start_time time_to_sleep = config['update_rate'] - time_taken if time_to_sleep > 0: - sleep(time_to_sleep) + await sleep(time_to_sleep) def exit_handler(self, signum, frame): self.modbus_handler.close() @@ -95,7 +98,7 @@ def init_registers(self): for register in config.get('holding', []): self.init_register('holding', register) - def read(self): + async def read(self): for table in self.registers: for address in list(self.registers[table].keys()): if time() - self.registers[table][address].get('last_fetch', 0) < config['update_rate']: @@ -112,7 +115,7 @@ def read(self): break logging.debug(f'read: table:{table} address:{address} count:{count}.') - result = self.modbus_handler.read(table, address, count) + result = await self.modbus_handler.read(table, address, count) for loop_address, register in enumerate(result, start=address): if loop_address not in self.registers[table]: @@ -165,9 +168,13 @@ def publish(self): register.get('retain', False)) +async def main(): + app = SungrowModbus2Mqtt() + await app.loop() + + if __name__ == '__main__': logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logging.getLogger('pymodbus').setLevel(logging.INFO) logging.info(f'starting SungrowModbus2Mqtt v{__version__}.') - app = SungrowModbus2Mqtt() - app.loop() + asyncio.run(main())