Skip to content

Commit ed1d92f

Browse files
authored
Merge pull request #49 from HotNoob/v1.1.5
V1.1.5
2 parents aea29e6 + 419aec4 commit ed1d92f

File tree

74 files changed

+1143
-11
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+1143
-11
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ srne_v3.9 = SRNE inverters - Untested
5050
victron_gx_3.3 = Victron GX Devices - Untested
5151
solark_v1.1 = SolarArk 8/12K Inverters - Untested
5252
hdhk_16ch_ac_module = some chinese current monitoring device :P
53+
srne_2021_v1.96 = SRNE inverters 2021+ (tested at ASF48100S200-H)
5354
5455
eg4_v58 = eg4 inverters ( EG4-6000XP ) - confirmed working
5556
eg4_3000ehv_v1 = eg4 inverters ( EG4_3000EHV )

classes/protocol_settings.py

+26-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import csv
22
from dataclasses import dataclass
33
from enum import Enum
4+
import glob
45
from typing import Union
56
from defs.common import strtoint
67
import itertools
@@ -300,7 +301,12 @@ def load__json(self, file : str = '', settings_dir : str = ''):
300301
if not file:
301302
file = self.protocol + '.json'
302303

303-
path = settings_dir + '/' + file
304+
path = self.find_protocol_file(file, settings_dir)
305+
306+
#if path does not exist; nothing to load. skip.
307+
if not path:
308+
print("ERROR: '"+file+"' not found")
309+
return
304310

305311
with open(path) as f:
306312
self.codes = json.loads(f.read())
@@ -618,6 +624,23 @@ def calculate_registry_ranges(self, map : list[registry_map_entry], max_register
618624
ranges.append((min(registers), max(registers)-min(registers)+1)) ## APPENDING A TUPLE!
619625

620626
return ranges
627+
def find_protocol_file(self, file : str, base_dir : str = '' ) -> str:
628+
629+
path = base_dir + '/' + file
630+
if os.path.exists(path):
631+
return path
632+
633+
suffix = file.split('_', 1)[0]
634+
635+
path = base_dir + '/' + suffix +'/' + file
636+
if os.path.exists(path):
637+
return path
638+
639+
#find file by name, recurisvely. last resort
640+
search_pattern = os.path.join(base_dir, '**', file)
641+
matches = glob.glob(search_pattern, recursive=True)
642+
return matches[0] if matches else None
643+
621644

622645
def load_registry_map(self, registry_type : Registry_Type, file : str = '', settings_dir : str = ''):
623646
if not settings_dir:
@@ -629,10 +652,10 @@ def load_registry_map(self, registry_type : Registry_Type, file : str = '', sett
629652
else:
630653
file = self.protocol + '.'+registry_type.name.lower()+'_registry_map.csv'
631654

632-
path = settings_dir + '/' + file
655+
path = self.find_protocol_file(file, settings_dir)
633656

634657
#if path does not exist; nothing to load. skip.
635-
if not os.path.exists(path):
658+
if not path:
636659
return
637660

638661
self.registry_map[registry_type] = self.load__registry(path, registry_type)

classes/transports/modbus_rtu.py

+37-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
import logging
22
from classes.protocol_settings import Registry_Type, protocol_settings
3-
from pymodbus.client.sync import ModbusSerialClient
3+
4+
import inspect
5+
6+
7+
try:
8+
from pymodbus.client.sync import ModbusSerialClient
9+
except ImportError:
10+
from pymodbus.client import ModbusSerialClient
11+
412
from .modbus_base import modbus_base
513
from configparser import SectionProxy
614
from defs.common import find_usb_serial_port, get_usb_serial_port_info, strtoint
@@ -11,6 +19,8 @@ class modbus_rtu(modbus_base):
1119
baudrate : int = 9600
1220
client : ModbusSerialClient
1321

22+
pymodbus_slave_arg = 'unit'
23+
1424
def __init__(self, settings : SectionProxy, protocolSettings : protocol_settings = None):
1525
#logger = logging.getLogger(__name__)
1626
#logging.basicConfig(level=logging.DEBUG)
@@ -33,16 +43,34 @@ def __init__(self, settings : SectionProxy, protocolSettings : protocol_settings
3343
address : int = settings.getint("address", 0)
3444
self.addresses = [address]
3545

36-
self.client = ModbusSerialClient(method='rtu', port=self.port,
37-
baudrate=int(self.baudrate),
38-
stopbits=1, parity='N', bytesize=8, timeout=2
39-
)
46+
# pymodbus compatability; unit was renamed to address
47+
if 'slave' in inspect.signature(ModbusSerialClient.read_holding_registers).parameters:
48+
self.pymodbus_slave_arg = 'slave'
49+
50+
51+
# Get the signature of the __init__ method
52+
init_signature = inspect.signature(ModbusSerialClient.__init__)
53+
54+
if 'method' in init_signature.parameters:
55+
self.client = ModbusSerialClient(method='rtu', port=self.port,
56+
baudrate=int(self.baudrate),
57+
stopbits=1, parity='N', bytesize=8, timeout=2
58+
)
59+
else:
60+
self.client = ModbusSerialClient(port=self.port,
61+
baudrate=int(self.baudrate),
62+
stopbits=1, parity='N', bytesize=8, timeout=2
63+
)
4064

4165
def read_registers(self, start, count=1, registry_type : Registry_Type = Registry_Type.INPUT, **kwargs):
4266

4367
if 'unit' not in kwargs:
4468
kwargs = {'unit': int(self.addresses[0]), **kwargs}
4569

70+
#compatability
71+
if self.pymodbus_slave_arg != 'unit':
72+
kwargs['slave'] = kwargs.pop('unit')
73+
4674
if registry_type == Registry_Type.INPUT:
4775
return self.client.read_input_registers(start, count, **kwargs)
4876
elif registry_type == Registry_Type.HOLDING:
@@ -55,6 +83,10 @@ def write_register(self, register : int, value : int, **kwargs):
5583
if 'unit' not in kwargs:
5684
kwargs = {'unit': self.addresses[0], **kwargs}
5785

86+
#compatability
87+
if self.pymodbus_slave_arg != 'unit':
88+
kwargs['slave'] = kwargs.pop('unit')
89+
5890
self.client.write_register(register, value, **kwargs) #function code 0x06 writes to holding register
5991

6092
def connect(self):

documentation/.scripts/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
folder for scripts releated to documentation
+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import os
2+
import urllib
3+
4+
5+
def extract_first_header(file_path):
6+
"""Extract the first header from a markdown file."""
7+
with open(file_path, "r", encoding="utf-8") as file:
8+
for line in file:
9+
line = line.strip()
10+
if line.startswith("#"):
11+
return line.replace('#', '')
12+
return None
13+
14+
15+
def generate_readme(directory : str, folder_order : str = [], output_file : str ="README.md"):
16+
with open(directory+'/'+output_file, "w", encoding="utf-8") as readme:
17+
readme.write("# README Index\n\n")
18+
readme.write("This README file contains an index of all files in the documentation directory.\n\n")
19+
readme.write("## File List\n\n")
20+
21+
note_file : str = directory+'/note.md'
22+
if os.path.exists(note_file):
23+
readme.write("\n## Additional Notes\n\n")
24+
with open(note_file, "r", encoding="utf-8") as note:
25+
readme.write(note.read())
26+
27+
28+
previous_folder = ""
29+
30+
folder_lines : dict[str, list[str]] = {}
31+
32+
for root, dirs, files in os.walk(directory):
33+
relative_folder = os.path.relpath(root, directory).replace("\\", "/") #use linux path structure
34+
35+
#exclude . folders
36+
if relative_folder[0] == '.':
37+
continue
38+
39+
if relative_folder != previous_folder:
40+
# Create a bold header for each new folder
41+
folder_lines[relative_folder] = []
42+
folder_lines[relative_folder].append(f"**{relative_folder}**\n\n")
43+
44+
previous_folder = relative_folder
45+
46+
#generate index in folder
47+
generate_readme(directory+"/"+relative_folder)
48+
49+
for file in files:
50+
file_path = os.path.relpath(os.path.join(root, file), directory).replace("\\", "/") #use linux path structure
51+
file_path = urllib.parse.quote(file_path)
52+
53+
if file == "README.md": #skip
54+
continue
55+
56+
if file.endswith(".md"):
57+
first_header = extract_first_header(os.path.join(root, file))
58+
if first_header:
59+
folder_lines[relative_folder].append(f"- [{file}]({file_path}) - {first_header}")
60+
else:
61+
folder_lines[relative_folder].append(f"- [{file}]({file_path})")
62+
else:
63+
folder_lines[relative_folder].append(f"- [{file}]({file_path})")
64+
65+
# Add an extra line break between different folders
66+
if files:
67+
folder_lines[relative_folder].append("")
68+
69+
#write output
70+
for folder in folder_lines:
71+
if folder in folder_order: #skip ordered folders for the end
72+
continue
73+
74+
for line in folder_lines[folder]:
75+
readme.write(line + "\n")
76+
77+
#write ordered output
78+
for folder in folder_order:
79+
if folder not in folder_lines: #not found
80+
continue
81+
82+
for line in folder_lines[folder]:
83+
readme.write(line + "\n")
84+
85+
86+
87+
if __name__ == "__main__":
88+
# Change the working directory to the location of the script
89+
script_dir = os.path.dirname(os.path.abspath(__file__))
90+
os.chdir(script_dir)
91+
92+
# Specify the directory you want to index
93+
directory_to_index = "../"
94+
generate_readme(directory_to_index, ["3rdparty", "3rdparty/protocols"])

documentation/3rdparty/README.md

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# README Index
2+
3+
This README file contains an index of all files in the documentation directory.
4+
5+
## File List
6+
7+
**protocols**
8+
9+
10+
- [CAN-Bus-protocol-PYLON-low-voltage-V1.2-20180408.pdf](protocols/CAN-Bus-protocol-PYLON-low-voltage-V1.2-20180408.pdf)
11+
- [converter.txt](protocols/converter.txt)
12+
- [EG4-3000-EHV - MODBUS Communication Protocol.pdf](protocols/EG4-3000-EHV%20-%20MODBUS%20Communication%20Protocol.pdf)
13+
- [EG4-6000XP-MODBUS-Communication-Protocol.pdf](protocols/EG4-6000XP-MODBUS-Communication-Protocol.pdf)
14+
- [Growatt Modbus Protocol v1.24.pdf](protocols/Growatt%20Modbus%20Protocol%20v1.24.pdf)
15+
- [Growatt PV Inverter Modbus RS485 RTU Protocol V3.04.pdf](protocols/Growatt%20PV%20Inverter%20Modbus%20RS485%20RTU%20Protocol%20V3.04.pdf)
16+
- [Growatt PV Inverter Modbus RS485 RTU Protocol V3.15.pdf](protocols/Growatt%20PV%20Inverter%20Modbus%20RS485%20RTU%20Protocol%20V3.15.pdf)
17+
- [hdhk_16ch_ac_module_modbus_rtu.jpeg](protocols/hdhk_16ch_ac_module_modbus_rtu.jpeg)
18+
- [hdhk_16ch_ac_module_modbus_rtu_chinese.pdf](protocols/hdhk_16ch_ac_module_modbus_rtu_chinese.pdf)
19+
- [hdhk_16ch_ac_module_modbus_rtu_translated_english.pdf](protocols/hdhk_16ch_ac_module_modbus_rtu_translated_english.pdf)
20+
- [MAX Series Modbus RTU Protocol.pdf](protocols/MAX%20Series%20Modbus%20RTU%20Protocol.pdf)
21+
- [note.md](protocols/note.md)
22+
- [OffGrid-Modbus-RS485RS232-RTU-Protocol-V0.14-20210420.pdf](protocols/OffGrid-Modbus-RS485RS232-RTU-Protocol-V0.14-20210420.pdf)
23+
- [PACE-BMS-Modbus-Protocol-for-RS485-V1.3-20170627.pdf](protocols/PACE-BMS-Modbus-Protocol-for-RS485-V1.3-20170627.pdf)
24+
- [PACE-BMS-RS485-communication-protocol-20180615.pdf](protocols/PACE-BMS-RS485-communication-protocol-20180615.pdf)
25+
- [PACE-CAN-communication-protocal(PACE-CAN-TY)-20161216-.pdf](protocols/PACE-CAN-communication-protocal%EF%BC%88PACE-CAN-TY%EF%BC%89-20161216-.pdf)
26+
- [PYLON LFP Battery communication protocol - RS485 V2.8 20161216.pdf](protocols/PYLON%20LFP%20Battery%20communication%20protocol%20-%20RS485%20V2.8%2020161216.pdf)
27+
- [RS485-protocol-pylon-low-voltage-V3.3-20180821.pdf](protocols/RS485-protocol-pylon-low-voltage-V3.3-20180821.pdf)
28+
- [Sigineer-Solar-Inverter-RS485-Port-Modbus-RTU-Protocol-v0.11-20200302.pdf](protocols/Sigineer-Solar-Inverter-RS485-Port-Modbus-RTU-Protocol-v0.11-20200302.pdf)
29+
- [Sol-Ark ModBus V1.1.pdf](protocols/Sol-Ark%20ModBus%20V1.1.pdf)
30+
- [SRNE_MODBUS_v3.9.pdf](protocols/SRNE_MODBUS_v3.9.pdf)
31+
- [Victron VE-Bus-products-MK2-Protocol-3-14.pdf](protocols/Victron%20VE-Bus-products-MK2-Protocol-3-14.pdf)
32+
- [Victron-CCGX-Modbus-TCP-register-list-3.30.xlsx](protocols/Victron-CCGX-Modbus-TCP-register-list-3.30.xlsx)
33+
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# README Index
2+
3+
This README file contains an index of all files in the documentation directory.
4+
5+
## File List
6+
7+
8+
## Additional Notes
9+
10+
11+
Protocol Documentation
12+
---
13+
[V3.14](Growatt%20PV%20Inverter%20Modbus%20RS485%20RTU%20Protocol%20V3.14.pdf)
14+
15+
Source: https://github.com/jrbenito/canadianSolar-pvoutput/blob/732efe68b71f67129f5b31442f82f2be0d79e605/docs/
16+
17+
Note: The original file name doesn't match the version specified in the document.
18+
19+
----
20+
[V3.04](Growatt%20PV%20Inverter%20Modbus%20RS485%20RTU%20Protocol%20V3.04.pdf)
21+
22+
Source: http://www.growatt.pl/dokumenty/Inne/Growatt%20PV%20Inverter%20Modbus%20RS485%20RTU%20Protocol%20V3.04.pdf
23+
File renamed without changes.
File renamed without changes.

documentation/README.md

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# README Index
2+
3+
This README file contains an index of all files in the documentation directory.
4+
5+
## File List
6+
7+
**dashboards**
8+
9+
10+
- [grafana.md](dashboards/grafana.md)
11+
- [homeassistant.md](dashboards/homeassistant.md)
12+
- [nodered.md](dashboards/nodered.md) - Setting up PythonProtocolGateway on a RasPi with NodeRed Dashboard 2.0
13+
14+
**devices**
15+
16+
17+
- [EG4.md](devices/EG4.md) - EG4 to MQTT
18+
- [Growatt.md](devices/Growatt.md) - Growatt To MQTT
19+
- [Sigineer.md](devices/Sigineer.md) - Sigineer to MQTT
20+
- [SOK.md](devices/SOK.md) - SOK to MQTT
21+
- [SolArk.md](devices/SolArk.md) - SolArk to MQTT
22+
23+
**usage**
24+
25+
26+
- [creating_and_editing_protocols.md](usage/creating_and_editing_protocols.md) - Creating and Editing Protocols ‐ JSON ‐ CSV
27+
- [protocols.md](usage/protocols.md) - Custom / Editing Protocols
28+
- [transports.md](usage/transports.md) - Transports
29+
30+
**usage/configuration_examples**
31+
32+
33+
- [modbus_rtu_to_modbus_tcp.md](usage/configuration_examples/modbus_rtu_to_modbus_tcp.md) - ModBus RTU to ModBus TCP
34+
- [modbus_rtu_to_mqtt.md](usage/configuration_examples/modbus_rtu_to_mqtt.md) - ModBus RTU to MQTT
35+
36+
**3rdparty**
37+
38+
39+
40+
**3rdparty/protocols**
41+
42+
43+
- [CAN-Bus-protocol-PYLON-low-voltage-V1.2-20180408.pdf](3rdparty/protocols/CAN-Bus-protocol-PYLON-low-voltage-V1.2-20180408.pdf)
44+
- [converter.txt](3rdparty/protocols/converter.txt)
45+
- [EG4-3000-EHV - MODBUS Communication Protocol.pdf](3rdparty/protocols/EG4-3000-EHV%20-%20MODBUS%20Communication%20Protocol.pdf)
46+
- [EG4-6000XP-MODBUS-Communication-Protocol.pdf](3rdparty/protocols/EG4-6000XP-MODBUS-Communication-Protocol.pdf)
47+
- [Growatt Modbus Protocol v1.24.pdf](3rdparty/protocols/Growatt%20Modbus%20Protocol%20v1.24.pdf)
48+
- [Growatt PV Inverter Modbus RS485 RTU Protocol V3.04.pdf](3rdparty/protocols/Growatt%20PV%20Inverter%20Modbus%20RS485%20RTU%20Protocol%20V3.04.pdf)
49+
- [Growatt PV Inverter Modbus RS485 RTU Protocol V3.15.pdf](3rdparty/protocols/Growatt%20PV%20Inverter%20Modbus%20RS485%20RTU%20Protocol%20V3.15.pdf)
50+
- [hdhk_16ch_ac_module_modbus_rtu.jpeg](3rdparty/protocols/hdhk_16ch_ac_module_modbus_rtu.jpeg)
51+
- [hdhk_16ch_ac_module_modbus_rtu_chinese.pdf](3rdparty/protocols/hdhk_16ch_ac_module_modbus_rtu_chinese.pdf)
52+
- [hdhk_16ch_ac_module_modbus_rtu_translated_english.pdf](3rdparty/protocols/hdhk_16ch_ac_module_modbus_rtu_translated_english.pdf)
53+
- [MAX Series Modbus RTU Protocol.pdf](3rdparty/protocols/MAX%20Series%20Modbus%20RTU%20Protocol.pdf)
54+
- [note.md](3rdparty/protocols/note.md)
55+
- [OffGrid-Modbus-RS485RS232-RTU-Protocol-V0.14-20210420.pdf](3rdparty/protocols/OffGrid-Modbus-RS485RS232-RTU-Protocol-V0.14-20210420.pdf)
56+
- [PACE-BMS-Modbus-Protocol-for-RS485-V1.3-20170627.pdf](3rdparty/protocols/PACE-BMS-Modbus-Protocol-for-RS485-V1.3-20170627.pdf)
57+
- [PACE-BMS-RS485-communication-protocol-20180615.pdf](3rdparty/protocols/PACE-BMS-RS485-communication-protocol-20180615.pdf)
58+
- [PACE-CAN-communication-protocal(PACE-CAN-TY)-20161216-.pdf](3rdparty/protocols/PACE-CAN-communication-protocal%EF%BC%88PACE-CAN-TY%EF%BC%89-20161216-.pdf)
59+
- [PYLON LFP Battery communication protocol - RS485 V2.8 20161216.pdf](3rdparty/protocols/PYLON%20LFP%20Battery%20communication%20protocol%20-%20RS485%20V2.8%2020161216.pdf)
60+
- [RS485-protocol-pylon-low-voltage-V3.3-20180821.pdf](3rdparty/protocols/RS485-protocol-pylon-low-voltage-V3.3-20180821.pdf)
61+
- [Sigineer-Solar-Inverter-RS485-Port-Modbus-RTU-Protocol-v0.11-20200302.pdf](3rdparty/protocols/Sigineer-Solar-Inverter-RS485-Port-Modbus-RTU-Protocol-v0.11-20200302.pdf)
62+
- [Sol-Ark ModBus V1.1.pdf](3rdparty/protocols/Sol-Ark%20ModBus%20V1.1.pdf)
63+
- [SRNE_MODBUS_v3.9.pdf](3rdparty/protocols/SRNE_MODBUS_v3.9.pdf)
64+
- [Victron VE-Bus-products-MK2-Protocol-3-14.pdf](3rdparty/protocols/Victron%20VE-Bus-products-MK2-Protocol-3-14.pdf)
65+
- [Victron-CCGX-Modbus-TCP-register-list-3.30.xlsx](3rdparty/protocols/Victron-CCGX-Modbus-TCP-register-list-3.30.xlsx)
66+

documentation/dashboards/README.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# README Index
2+
3+
This README file contains an index of all files in the documentation directory.
4+
5+
## File List
6+

documentation/dashboards/grafana.md

Whitespace-only changes.

documentation/dashboards/homeassistant.md

Whitespace-only changes.

0 commit comments

Comments
 (0)