Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c54748a
base support for SRNE inverters 2021+
gorbyo Aug 8, 2024
5f2b83c
Merge pull request #45 from gorbyo/main
HotNoob Aug 8, 2024
37cd3e2
updated naming of vars
gorbyo Aug 15, 2024
cf52dca
srne2021 - add some statistics
gorbyo Aug 15, 2024
356c21e
Merge pull request #47 from gorbyo/main
HotNoob Aug 15, 2024
12e9dd3
srne2021 - fix syntax errors
gorbyo Aug 17, 2024
1a1673a
Merge pull request #48 from gorbyo/main
HotNoob Aug 17, 2024
f3ea551
wiki to repo
HotNoob Aug 17, 2024
9a1dc5a
Create index.md
HotNoob Aug 17, 2024
354cfa3
rename
HotNoob Aug 17, 2024
dfacca8
Delete readme.md
HotNoob Aug 17, 2024
757c730
documentation index script
HotNoob Aug 17, 2024
2801fe2
fix paths
HotNoob Aug 17, 2024
fc88974
Update README.md
HotNoob Aug 17, 2024
df2be75
header fix
HotNoob Aug 17, 2024
c89b5c4
Merge branch 'v1.1.5' of https://github.com/HotNoob/PythonProtocolGat…
HotNoob Aug 17, 2024
798f7e5
Update EG4.md
HotNoob Aug 17, 2024
39456f0
fix titles
HotNoob Aug 17, 2024
9e30e24
fix links
HotNoob Aug 17, 2024
75e2fea
subfolder indexes
HotNoob Aug 17, 2024
4960645
remove readme.md indexes
HotNoob Aug 17, 2024
66bc871
fix header
HotNoob Aug 17, 2024
85f903d
folder organization for protocols
HotNoob Aug 17, 2024
243d4d0
support for newer versions on pymodbus
HotNoob Aug 17, 2024
bc3e9d9
pymodbus compatability
HotNoob Aug 17, 2024
63ed627
pymodbus update compatability
HotNoob Aug 17, 2024
7596232
fix kwarg
HotNoob Aug 17, 2024
59cce7f
added support for pymodbus 2.3 to 3.7
HotNoob Aug 17, 2024
419aec4
add pyserial requirement
HotNoob Sep 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ srne_v3.9 = SRNE inverters - Untested
victron_gx_3.3 = Victron GX Devices - Untested
solark_v1.1 = SolarArk 8/12K Inverters - Untested
hdhk_16ch_ac_module = some chinese current monitoring device :P
srne_2021_v1.96 = SRNE inverters 2021+ (tested at ASF48100S200-H)
eg4_v58 = eg4 inverters ( EG4-6000XP ) - confirmed working
eg4_3000ehv_v1 = eg4 inverters ( EG4_3000EHV )
Expand Down
29 changes: 26 additions & 3 deletions classes/protocol_settings.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import csv
from dataclasses import dataclass
from enum import Enum
import glob
from typing import Union
from defs.common import strtoint
import itertools
Expand Down Expand Up @@ -300,7 +301,12 @@ def load__json(self, file : str = '', settings_dir : str = ''):
if not file:
file = self.protocol + '.json'

path = settings_dir + '/' + file
path = self.find_protocol_file(file, settings_dir)

#if path does not exist; nothing to load. skip.
if not path:
print("ERROR: '"+file+"' not found")
return

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

return ranges
def find_protocol_file(self, file : str, base_dir : str = '' ) -> str:

path = base_dir + '/' + file
if os.path.exists(path):
return path

suffix = file.split('_', 1)[0]

path = base_dir + '/' + suffix +'/' + file
if os.path.exists(path):
return path

#find file by name, recurisvely. last resort
search_pattern = os.path.join(base_dir, '**', file)
matches = glob.glob(search_pattern, recursive=True)
return matches[0] if matches else None


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

path = settings_dir + '/' + file
path = self.find_protocol_file(file, settings_dir)

#if path does not exist; nothing to load. skip.
if not os.path.exists(path):
if not path:
return

self.registry_map[registry_type] = self.load__registry(path, registry_type)
Expand Down
42 changes: 37 additions & 5 deletions classes/transports/modbus_rtu.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import logging
from classes.protocol_settings import Registry_Type, protocol_settings
from pymodbus.client.sync import ModbusSerialClient

import inspect


try:
from pymodbus.client.sync import ModbusSerialClient
except ImportError:
from pymodbus.client import ModbusSerialClient

from .modbus_base import modbus_base
from configparser import SectionProxy
from defs.common import find_usb_serial_port, get_usb_serial_port_info, strtoint
Expand All @@ -11,6 +19,8 @@ class modbus_rtu(modbus_base):
baudrate : int = 9600
client : ModbusSerialClient

pymodbus_slave_arg = 'unit'

def __init__(self, settings : SectionProxy, protocolSettings : protocol_settings = None):
#logger = logging.getLogger(__name__)
#logging.basicConfig(level=logging.DEBUG)
Expand All @@ -33,16 +43,34 @@ def __init__(self, settings : SectionProxy, protocolSettings : protocol_settings
address : int = settings.getint("address", 0)
self.addresses = [address]

self.client = ModbusSerialClient(method='rtu', port=self.port,
baudrate=int(self.baudrate),
stopbits=1, parity='N', bytesize=8, timeout=2
)
# pymodbus compatability; unit was renamed to address
if 'slave' in inspect.signature(ModbusSerialClient.read_holding_registers).parameters:
self.pymodbus_slave_arg = 'slave'


# Get the signature of the __init__ method
init_signature = inspect.signature(ModbusSerialClient.__init__)

if 'method' in init_signature.parameters:
self.client = ModbusSerialClient(method='rtu', port=self.port,
baudrate=int(self.baudrate),
stopbits=1, parity='N', bytesize=8, timeout=2
)
else:
self.client = ModbusSerialClient(port=self.port,
baudrate=int(self.baudrate),
stopbits=1, parity='N', bytesize=8, timeout=2
)

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

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

#compatability
if self.pymodbus_slave_arg != 'unit':
kwargs['slave'] = kwargs.pop('unit')

if registry_type == Registry_Type.INPUT:
return self.client.read_input_registers(start, count, **kwargs)
elif registry_type == Registry_Type.HOLDING:
Expand All @@ -55,6 +83,10 @@ def write_register(self, register : int, value : int, **kwargs):
if 'unit' not in kwargs:
kwargs = {'unit': self.addresses[0], **kwargs}

#compatability
if self.pymodbus_slave_arg != 'unit':
kwargs['slave'] = kwargs.pop('unit')

self.client.write_register(register, value, **kwargs) #function code 0x06 writes to holding register

def connect(self):
Expand Down
1 change: 1 addition & 0 deletions documentation/.scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
folder for scripts releated to documentation
94 changes: 94 additions & 0 deletions documentation/.scripts/generate_indexes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import os
import urllib


def extract_first_header(file_path):
"""Extract the first header from a markdown file."""
with open(file_path, "r", encoding="utf-8") as file:
for line in file:
line = line.strip()
if line.startswith("#"):
return line.replace('#', '')
return None


def generate_readme(directory : str, folder_order : str = [], output_file : str ="README.md"):
with open(directory+'/'+output_file, "w", encoding="utf-8") as readme:
readme.write("# README Index\n\n")
readme.write("This README file contains an index of all files in the documentation directory.\n\n")
readme.write("## File List\n\n")

note_file : str = directory+'/note.md'
if os.path.exists(note_file):
readme.write("\n## Additional Notes\n\n")
with open(note_file, "r", encoding="utf-8") as note:
readme.write(note.read())


previous_folder = ""

folder_lines : dict[str, list[str]] = {}

for root, dirs, files in os.walk(directory):
relative_folder = os.path.relpath(root, directory).replace("\\", "/") #use linux path structure

#exclude . folders
if relative_folder[0] == '.':
continue

if relative_folder != previous_folder:
# Create a bold header for each new folder
folder_lines[relative_folder] = []
folder_lines[relative_folder].append(f"**{relative_folder}**\n\n")

previous_folder = relative_folder

#generate index in folder
generate_readme(directory+"/"+relative_folder)

for file in files:
file_path = os.path.relpath(os.path.join(root, file), directory).replace("\\", "/") #use linux path structure
file_path = urllib.parse.quote(file_path)

if file == "README.md": #skip
continue

if file.endswith(".md"):
first_header = extract_first_header(os.path.join(root, file))
if first_header:
folder_lines[relative_folder].append(f"- [{file}]({file_path}) - {first_header}")
else:
folder_lines[relative_folder].append(f"- [{file}]({file_path})")
else:
folder_lines[relative_folder].append(f"- [{file}]({file_path})")

# Add an extra line break between different folders
if files:
folder_lines[relative_folder].append("")

#write output
for folder in folder_lines:
if folder in folder_order: #skip ordered folders for the end
continue

for line in folder_lines[folder]:
readme.write(line + "\n")

#write ordered output
for folder in folder_order:
if folder not in folder_lines: #not found
continue

for line in folder_lines[folder]:
readme.write(line + "\n")



if __name__ == "__main__":
# Change the working directory to the location of the script
script_dir = os.path.dirname(os.path.abspath(__file__))
os.chdir(script_dir)

# Specify the directory you want to index
directory_to_index = "../"
generate_readme(directory_to_index, ["3rdparty", "3rdparty/protocols"])
33 changes: 33 additions & 0 deletions documentation/3rdparty/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# README Index

This README file contains an index of all files in the documentation directory.

## File List

**protocols**


- [CAN-Bus-protocol-PYLON-low-voltage-V1.2-20180408.pdf](protocols/CAN-Bus-protocol-PYLON-low-voltage-V1.2-20180408.pdf)
- [converter.txt](protocols/converter.txt)
- [EG4-3000-EHV - MODBUS Communication Protocol.pdf](protocols/EG4-3000-EHV%20-%20MODBUS%20Communication%20Protocol.pdf)
- [EG4-6000XP-MODBUS-Communication-Protocol.pdf](protocols/EG4-6000XP-MODBUS-Communication-Protocol.pdf)
- [Growatt Modbus Protocol v1.24.pdf](protocols/Growatt%20Modbus%20Protocol%20v1.24.pdf)
- [Growatt PV Inverter Modbus RS485 RTU Protocol V3.04.pdf](protocols/Growatt%20PV%20Inverter%20Modbus%20RS485%20RTU%20Protocol%20V3.04.pdf)
- [Growatt PV Inverter Modbus RS485 RTU Protocol V3.15.pdf](protocols/Growatt%20PV%20Inverter%20Modbus%20RS485%20RTU%20Protocol%20V3.15.pdf)
- [hdhk_16ch_ac_module_modbus_rtu.jpeg](protocols/hdhk_16ch_ac_module_modbus_rtu.jpeg)
- [hdhk_16ch_ac_module_modbus_rtu_chinese.pdf](protocols/hdhk_16ch_ac_module_modbus_rtu_chinese.pdf)
- [hdhk_16ch_ac_module_modbus_rtu_translated_english.pdf](protocols/hdhk_16ch_ac_module_modbus_rtu_translated_english.pdf)
- [MAX Series Modbus RTU Protocol.pdf](protocols/MAX%20Series%20Modbus%20RTU%20Protocol.pdf)
- [note.md](protocols/note.md)
- [OffGrid-Modbus-RS485RS232-RTU-Protocol-V0.14-20210420.pdf](protocols/OffGrid-Modbus-RS485RS232-RTU-Protocol-V0.14-20210420.pdf)
- [PACE-BMS-Modbus-Protocol-for-RS485-V1.3-20170627.pdf](protocols/PACE-BMS-Modbus-Protocol-for-RS485-V1.3-20170627.pdf)
- [PACE-BMS-RS485-communication-protocol-20180615.pdf](protocols/PACE-BMS-RS485-communication-protocol-20180615.pdf)
- [PACE-CAN-communication-protocal(PACE-CAN-TY)-20161216-.pdf](protocols/PACE-CAN-communication-protocal%EF%BC%88PACE-CAN-TY%EF%BC%89-20161216-.pdf)
- [PYLON LFP Battery communication protocol - RS485 V2.8 20161216.pdf](protocols/PYLON%20LFP%20Battery%20communication%20protocol%20-%20RS485%20V2.8%2020161216.pdf)
- [RS485-protocol-pylon-low-voltage-V3.3-20180821.pdf](protocols/RS485-protocol-pylon-low-voltage-V3.3-20180821.pdf)
- [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)
- [Sol-Ark ModBus V1.1.pdf](protocols/Sol-Ark%20ModBus%20V1.1.pdf)
- [SRNE_MODBUS_v3.9.pdf](protocols/SRNE_MODBUS_v3.9.pdf)
- [Victron VE-Bus-products-MK2-Protocol-3-14.pdf](protocols/Victron%20VE-Bus-products-MK2-Protocol-3-14.pdf)
- [Victron-CCGX-Modbus-TCP-register-list-3.30.xlsx](protocols/Victron-CCGX-Modbus-TCP-register-list-3.30.xlsx)

23 changes: 23 additions & 0 deletions documentation/3rdparty/protocols/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# README Index

This README file contains an index of all files in the documentation directory.

## File List


## Additional Notes


Protocol Documentation
---
[V3.14](Growatt%20PV%20Inverter%20Modbus%20RS485%20RTU%20Protocol%20V3.14.pdf)

Source: https://github.com/jrbenito/canadianSolar-pvoutput/blob/732efe68b71f67129f5b31442f82f2be0d79e605/docs/

Note: The original file name doesn't match the version specified in the document.

----
[V3.04](Growatt%20PV%20Inverter%20Modbus%20RS485%20RTU%20Protocol%20V3.04.pdf)

Source: http://www.growatt.pl/dokumenty/Inne/Growatt%20PV%20Inverter%20Modbus%20RS485%20RTU%20Protocol%20V3.04.pdf

File renamed without changes.
File renamed without changes.
File renamed without changes.
66 changes: 66 additions & 0 deletions documentation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# README Index

This README file contains an index of all files in the documentation directory.

## File List

**dashboards**


- [grafana.md](dashboards/grafana.md)
- [homeassistant.md](dashboards/homeassistant.md)
- [nodered.md](dashboards/nodered.md) - Setting up PythonProtocolGateway on a RasPi with NodeRed Dashboard 2.0

**devices**


- [EG4.md](devices/EG4.md) - EG4 to MQTT
- [Growatt.md](devices/Growatt.md) - Growatt To MQTT
- [Sigineer.md](devices/Sigineer.md) - Sigineer to MQTT
- [SOK.md](devices/SOK.md) - SOK to MQTT
- [SolArk.md](devices/SolArk.md) - SolArk to MQTT

**usage**


- [creating_and_editing_protocols.md](usage/creating_and_editing_protocols.md) - Creating and Editing Protocols ‐ JSON ‐ CSV
- [protocols.md](usage/protocols.md) - Custom / Editing Protocols
- [transports.md](usage/transports.md) - Transports

**usage/configuration_examples**


- [modbus_rtu_to_modbus_tcp.md](usage/configuration_examples/modbus_rtu_to_modbus_tcp.md) - ModBus RTU to ModBus TCP
- [modbus_rtu_to_mqtt.md](usage/configuration_examples/modbus_rtu_to_mqtt.md) - ModBus RTU to MQTT

**3rdparty**



**3rdparty/protocols**


- [CAN-Bus-protocol-PYLON-low-voltage-V1.2-20180408.pdf](3rdparty/protocols/CAN-Bus-protocol-PYLON-low-voltage-V1.2-20180408.pdf)
- [converter.txt](3rdparty/protocols/converter.txt)
- [EG4-3000-EHV - MODBUS Communication Protocol.pdf](3rdparty/protocols/EG4-3000-EHV%20-%20MODBUS%20Communication%20Protocol.pdf)
- [EG4-6000XP-MODBUS-Communication-Protocol.pdf](3rdparty/protocols/EG4-6000XP-MODBUS-Communication-Protocol.pdf)
- [Growatt Modbus Protocol v1.24.pdf](3rdparty/protocols/Growatt%20Modbus%20Protocol%20v1.24.pdf)
- [Growatt PV Inverter Modbus RS485 RTU Protocol V3.04.pdf](3rdparty/protocols/Growatt%20PV%20Inverter%20Modbus%20RS485%20RTU%20Protocol%20V3.04.pdf)
- [Growatt PV Inverter Modbus RS485 RTU Protocol V3.15.pdf](3rdparty/protocols/Growatt%20PV%20Inverter%20Modbus%20RS485%20RTU%20Protocol%20V3.15.pdf)
- [hdhk_16ch_ac_module_modbus_rtu.jpeg](3rdparty/protocols/hdhk_16ch_ac_module_modbus_rtu.jpeg)
- [hdhk_16ch_ac_module_modbus_rtu_chinese.pdf](3rdparty/protocols/hdhk_16ch_ac_module_modbus_rtu_chinese.pdf)
- [hdhk_16ch_ac_module_modbus_rtu_translated_english.pdf](3rdparty/protocols/hdhk_16ch_ac_module_modbus_rtu_translated_english.pdf)
- [MAX Series Modbus RTU Protocol.pdf](3rdparty/protocols/MAX%20Series%20Modbus%20RTU%20Protocol.pdf)
- [note.md](3rdparty/protocols/note.md)
- [OffGrid-Modbus-RS485RS232-RTU-Protocol-V0.14-20210420.pdf](3rdparty/protocols/OffGrid-Modbus-RS485RS232-RTU-Protocol-V0.14-20210420.pdf)
- [PACE-BMS-Modbus-Protocol-for-RS485-V1.3-20170627.pdf](3rdparty/protocols/PACE-BMS-Modbus-Protocol-for-RS485-V1.3-20170627.pdf)
- [PACE-BMS-RS485-communication-protocol-20180615.pdf](3rdparty/protocols/PACE-BMS-RS485-communication-protocol-20180615.pdf)
- [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)
- [PYLON LFP Battery communication protocol - RS485 V2.8 20161216.pdf](3rdparty/protocols/PYLON%20LFP%20Battery%20communication%20protocol%20-%20RS485%20V2.8%2020161216.pdf)
- [RS485-protocol-pylon-low-voltage-V3.3-20180821.pdf](3rdparty/protocols/RS485-protocol-pylon-low-voltage-V3.3-20180821.pdf)
- [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)
- [Sol-Ark ModBus V1.1.pdf](3rdparty/protocols/Sol-Ark%20ModBus%20V1.1.pdf)
- [SRNE_MODBUS_v3.9.pdf](3rdparty/protocols/SRNE_MODBUS_v3.9.pdf)
- [Victron VE-Bus-products-MK2-Protocol-3-14.pdf](3rdparty/protocols/Victron%20VE-Bus-products-MK2-Protocol-3-14.pdf)
- [Victron-CCGX-Modbus-TCP-register-list-3.30.xlsx](3rdparty/protocols/Victron-CCGX-Modbus-TCP-register-list-3.30.xlsx)

6 changes: 6 additions & 0 deletions documentation/dashboards/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# README Index

This README file contains an index of all files in the documentation directory.

## File List

Empty file.
Empty file.
Loading
Loading