Skip to content

Commit

Permalink
v1.0.10-dev-async: use async, no sungrow encryption
Browse files Browse the repository at this point in the history
  • Loading branch information
jesseklm committed Aug 6, 2024
1 parent 6f251ae commit 06f222f
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 26 deletions.
5 changes: 1 addition & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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" ]
21 changes: 10 additions & 11 deletions modbus_handler.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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

Expand Down
2 changes: 1 addition & 1 deletion pypy.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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 && \
Expand Down
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
pymodbus~=3.7.0
SungrowModbusTcpClient-jesseklm~=0.1.9
paho-mqtt~=2.1.0
PyYAML~=6.0.1
25 changes: 16 additions & 9 deletions sungrowmodbus2mqtt.py
Original file line number Diff line number Diff line change
@@ -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:
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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']:
Expand All @@ -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]:
Expand Down Expand Up @@ -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())

0 comments on commit 06f222f

Please sign in to comment.